gallery/earth origami

eightyeight

 

a name and a number and a place and a door and a website

including science, physics, mechanics, engineering ...

of all things.

Tech and technology



/*
        T D 5   E n g i n e    S e n s o r s

to read critical temperatures and pressures and send to CAN-bus shield for ELM327 and then Android display.
Respond to out-of-limit Ts & DPs in Arduino: Out-of-Limit (OOL) parameters will cause Advisory, Alarm, Imminent or "Critical engine off".

Advisory: visual note on display (and beep?)
Alarm: long audio siren and "Indication"
Imminent: Alarm, plus second alarm and further Critical if further deterioration of any (other) sensor data
Critical: Engine OFF without further warning and

Parameters:
                             Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti   sensor  Pin    Mnemonic
Sensor reference/supply 5V      s_5V      V        4.5     5.0     5.2     5.3     4.75                     analog  A0      s_5V
Exhaust Gas Temperature         EGT      �C        200     450     700     600     650     670      800     MAX1spi D6      EGT
Cylinder Head Temperature       CHT      �C        50      110     180     170     180     180      230     MAX2spi D7      CHT
Engine Oil Temperature          EOT      �C        30       80     100      90      95     100      115     DS18B20 D10     EOT
Engine Oil Pressure             EOP      Bar       0.5     1-3      4      1.5      1      0.8      0.7     OPS     A1      EOP
Engine Coolant Temperature      ECT      �C        30       75      95      90      95     99       102     NTC     A2      ECT   
Turbo Differential Pressure     TDP      Bar        0       0       10                                      MPX     A3      TDP
Engine Coolant Level            ECL      Arb        1       4       5       2       1      0.5      0.3     switch  A4      ECL

//SPI: CLK CS DATA
//I2C: 1-wire DATA

MAX6675    SPI thermocouple interface, direct output in �C. 200ms read cycle time. EGT and CHT
DS18B20    I2C temperature sensor
Univeral Pressure sensor - car type 10 - 180ohm for oil pressure 0 - 250kPa, 100kPa = 1Bar
NTC temperature sensor 10kohm, 1k ~30�C, LR TD5 original ECT paralleled to ECU, no PULLUP (there is one, provided by ECU)
MPX4250DP  proportional analog output pressure sensor (DP version is differential with vacuum-side port)
coolant level sensor reed float switch mounted in wall of Expansion Tank.

MCP2515    CAN-bus shield will take our Arduino sensor data and shake it up into OBD formated data, MCP runs SPI, CS on pin D9
EmCut      Relay to disable fuel pump in Critical condition (eg. No oil pressure)
*/      

/************************************************************************/
/*                             I N C L U D E S                          */
/************************************************************************/

#include <max6675.h>              //Thermocouple devices on SPI
#include <OneWire.h>              //I2C
#include <DallasTemperature.h>    //DS18B20 temperature sensor on OneWire I2C
#include <mcp_can.h>              //CAN-bus shield, MCP2525 CAN-bus controller


/************************************************************************/
/*                   P R O G R A M    V A R I A B L E S                 */
/************************************************************************/
int i;                       //loop counter

//pin A0                                   Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti
//Sensor reference/supply 5V                 5Vss      V        4.5     5.0     5.2     5.3     4.75                   
float s_5V_data  = 0.0;      //from Analog input direct ADC result, maths ADC to Volts. pin A0.
float s_5V       = 5.0;      //resulting real s_5V  V O l T A G E, needed to measure the s_5V, so start with 5.0
float s_5Vmin    = 4.5;      
float s_5Vmax    = 5.1;
float s_5Vadv    = 5.2;
float s_5Valm    = 4.7;
//s_5V is used in formulas for parsing sensor data where sensor Vss is required, eg EOP and TDP.

//pin D6                                   Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti
//Exhaust Gas Temperature                    EGT       �C       200     450     700     600     650     670      800  
int   EGT_data     = 0;      //from sensor, data from MAX6675 I2C thermocouple reader CS = pin D6
int   EGT          = 0;      //resulting real EGT    T E M P E R A T U R E ,   �C
int   EGTmin       = 200;
int   EGTmax       = 700;
int   EGTadv       = 600;
int   EGTalm       = 650;
int   EGTimm       = 670;
int   EGT_critical = 800;
byte  EGT_flag     = 0;      //extra data for result of Stutas Analysis, resulting in alarm or EmCut. Byte for Andorid

//pin D7                                   Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti
//Cylinder Head Temperature                  CHT      �C        50      130     180     170     180     190      230
int   CHT_data     = 0;      //from sensor, data from MAX6675 I2C thermocouple reader CS = pin D7
int   CHT          = 0;      //resulting real CHT    T E M P E R A T U R E ,   �c
int   CHTmin       = 50;
int   CHTmax       = 180;
int   CHTadv       = 170;
int   CHTalm       = 180;
int   CHTimm       = 190;
byte  CHT_critical = 230;
byte  CHT_flag     = 0;

//pin D10                                   Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti
//Engine Oil Temperature                     ECT      �C        30       80     100      90      95     100       115  
float EOT_data   = 0;      //from DS18B20 I2C sensor, I2C 1wire DATA on pin D10
byte  EOT          = 0;      //resulting real ECT   T E M P E R A T U R E ,   �c
int   EOTmin       = 30;
int   EOTmax       = 100;
int   EOTadv       = 90;
int   EOTalm       = 95;
int   EOTimm       = 100;
byte  EOT_critical = 115;
byte  EOT_flag     = 0;

//pin A1                                   Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti  
//Engine Oil Pressure                        EOP      Bar       0.5     1-3      4      1.5      1      0.8      0.7  
float EOP_data     = 0.0;  //from sensor, ADC result, analog 0 - 1023 analog ADC pin A1
float EOP          = 0.0;  //resulting real EOP   P R E S S U R E ,    Bar units 1BAR = 100kPa
float EOPmin       = 0.5;
float EOPmax       = 4.0;
float EOPadv       = 1.5;
float EOPalm       = 1.0;
float EOPimm       = 0.8;
float EOP_critical = 0.3;
byte  EPO_flag     = 0;

//pin A2                                       Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti
//Engine Coolant Temperature                     ECT      �C        30       80      95      90      95     99       102   
int   ECT_data     = 0;      //from analogue sensor, original Land Rover TD5 sensor, ntc on pin A2
byte  ECT          = 0;      //resulting real ECT   T E M P E R A T U R E ,   �c
int   ECTmin       = 30;
int   ECTmax       = 95;
int   ECTadv       = 90;
int   ECTalm       = 95;
int   ECTimm       = 99;
byte  ECT_critical = 102;
byte  ECT_flag     = 0;

//pin A3                                    Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti
//Turbo Differential Pressure                 Bar        0       0       2       4               3                     
int   TDP_data     = 0;      //from analogue MCP4250DP sensor, ADC result on pin A3
byte  TDP          = 0;      //resulting real TDP   P R E S S U R E ,   Bar
int   TDPmin       = 0;
int   TDPmax       = 4;
byte  TDP_critical = 3;
byte  TDP_flag     = 0;

//pin A4                                   Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti
//Engine Coolant Level                       ECL      Arb        1       4       5       2       1      0.5      0.3  
//reed level float switch on expansion tank to detect low level coolant
byte  ECL          = 0;  //from low level switch                  digital read on analog pin A4
byte  ECL_critical = 0;  //check if reed is NO with good coolant level
byte  ECL_flag     = 0;


//MAX6676 considerations                //if MAX6675 is polled too frequently, result is it never answers at all.
                                       //   .. each poll starts the "read" process so, ask again and it never answers before it starts over.
int TC_interval         = 300;          //limit number of reads required from MAX6675. Max 5 per second = one every 200ms.
int TC_time;                            //by timing between each read cycle.

//MPX4250DP considerations              //MPX device measure pressure over range 0 - 250kPa (0 - 2.5 bar) over 0 - 5V full range output.
int MPX_TDP             = 0;            //initial data read from analogue MCP4250 pressure sensor.

//CANbus OBD shield and stuff
//int CAN_SPI_CSpin       = 9;
byte canID                = 0x00;         //not sure why we want a CAN id and a CAN tx id... or even if .
int txID                  = 0x07E8;       //id for our new tx ECU. This is also the address to which
                                         //polling will be sent by the scan tool / Android Torque. I THINK.
byte len                  = 0;
byte msgBuffer[8];                        //incoming messages, soliciting data received here  
String BuildMessage       = "";
int MSGIdentifier         = 0;


/* example CAN-bus OBD message:
CAN.sendMsgBuf(INT32U txID, INT8U ext, INT8U len, INT8U *buf)
txID is the CAN-bus address.
ext '0' means standard frame. '1' means extended frame.
len represents the length number of bytes in this frame.
buf is the data-content of this message.

CAN.sendMsgBuf(txID, 0, len, OBDmsg)
OBDmsg is the parsed data from the sensor in the format:
[PID, data, status]
OBD says the data may be more than one byte but the number of bytes in len, must correspond.
Our PID�s are all one byte so len will be 3. (its not an array, so not n-bytes + 1).
*/

//CAN-bus OBD PID's:
//Mode01:
byte PID_EGT                  = 0x6B;          //Exhaust Gas Temperature     (Exhaust Gas Recirculation Temperature)
byte PID_CHT                  = 0x84;          //Cylinder Head Temperature   (Manifold Surface Temperature)
byte PID_EOT                  = 0x5C;          //Engine Oil Temperature
byte PID_EOP                  = 0x0B;          //Engine Oil Pressure          (Inlet Manifold Absolute Pressure)
byte PID_ECT                  = 0x05;          //Formula A-40
byte PID_TDP                  = 0x6F;          //Turbo Differential Pressure (Turbo Compressor Inlet Pressure)
byte PID_ECL                  = 0x1E;          //Engine Coolant Level alarm  (Auxiliary Input Status, PTO)      

byte PID_supported[8]         = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; //...if we're asked which PID wwe support, its all of them!
byte PID_MILcleared[7]        = {4, 65, 63, 34, 224, 185, 147};

//CAN Messages
byte rxd_message[8];
byte send_message[8];
int which_can_msg             = 0;             //identifer for us, dependent on CAN message recieved (and its PID)
byte EGTmsg[8]                   = {4, 65, PID_EGT, EGT, EGT_flag};

byte CHTmsg[7]                   = {4, 65, PID_CHT, CHT, 0, 185, 147};
/*
byte EOTmsg[]                   = {4, 65, PID_EOT, EOT, EOT_critical};
byte EOPmsg[]                   = {4, 65, PID_EOP, EOP, EOP_critical};
byte ECTmsg[]                   = {4, 65, PID_ACT, ECT, ECT_critical};
byte TDPmsg[]                   = {4, 65, PID_TDP, TDP, TDP_critical};
byte ECLmsg[]                   = {4, 65, PID_ECL, ECL, ECL_critical};
*/

//NTC considerations:
int NTCohms                   = 10000;         //resitance value of NTC at nominal degC
int NTCnominaldegC            = 25;
int numsamples                = 5;             //NTC is noisey, so take the mean of (5) samples
int ntcBcoefficient           = 3950;          //used in NTC maths, arbitrary really, Beta coef for the TD5's NTC coolant temp sensor is not available.
int SeriesRohms               = 10000;         //assume TD5 ECU uses 10K series with the 10K NTC
uint16_t samples[5];                           //for convenience, the successive sample values will go in this array
                                              //numsamples as variable in array definition requires integer
//LED FLASHER, STATE ENGINE
unsigned long redLEDFinterval = 300;
unsigned long redLEDSinterval = 1000;

unsigned long redLEDtimer     = 0;             //time instance during flash

bool redLEDon                 = 0;
bool redLEDfast               = 0;             //0 = slow, 1 = fast.
bool redLEDflash              = 0;             //0 = static, 1 = flash.

/************************************************************************/
/*                             D E V I C E S                            */
/************************************************************************/

//Create and connect to DEVICES
//                    H A R D W A R E    P I N    N U M B E R S
int s_5V_pin                  = A0;            //voltage, analog ADC input
int EOP_pin                   = A1;            //OPS, Oil Pressure Sensor
int ECT_pin                   = A2;            //LR TD5 coolant temperature NTC
int TDP_pin                   = A3;            //NXP MPX4250DP pressure sensor, analog ADC input
int ECL_pin                   = A4;            //switch: reed switch, digital input

//TC_EGT Thermocouple MAX6675_1
int TC_CLK                    = 4;             //SPI bus Clock common
int TC_DO                     = 5;             //SPI bus Data Out common
int TC_EGT_CS                 = 6;             //SPI EGT specific Chip Select

//TC_CHT Thermocouple MAX6675_2
int TC_CHT_CS                 = 7;             //SPI CHT specific CS

int CAN_SPI_CSpin             = 9;             //SPI CAN_bus SHIELD CS

int I2C_data_pin              = 10;            //I2C OneWire Data bus

int EmCut_pin                 = 12;            //Output to relay to Emergency Cut, fuel pump off, *** ENGINE STOP ***

int redLED_pin                = 13;            //on-board red LED

/*
                  ***** E N G I N E   S T O P   if *****
              1) Oil Pressure below critical
         or   2) CHT Cylinder Head Temperature above CRITICAL
         or   3) ECT Engine Coolant Temperature above CRITICAL
         or   4) EGT Exhaust Gas Temperature and ECT both above ALARM

                        *************************
*/

/************************************************************************/
/*                   D E V I C E    I N S T A N C E S                   */
/************************************************************************/

//Dallas Temperature
DeviceAddress DS_EOT_address;                  //Dallas Temperature

//Thermocouples
//digital pins:  4        6,7       5          
MAX6675 TC_EGT(TC_CLK, TC_EGT_CS, TC_DO);      //Exhaust Gas Temp       CS = 7
MAX6675 TC_CHT(TC_CLK, TC_CHT_CS, TC_DO);      //Cylinder Head Temp     CS = 6

MCP_CAN CAN(CAN_SPI_CSpin);                    //CAN_bus Shield service CS = 9

OneWire I2C(I2C_data_pin);                     //I2C OneWire service  data = 10

DallasTemperature DS_EOT(&I2C);                //Dallas Temperature instigated for EOT on OneWire bus


/************************************************************************/
/*                               S E T U P                              */
/************************************************************************/
void setup()
{
//Serial.begin(38400);
if (CAN_OK == CAN.begin(CAN_500KBPS))
  {
  }

pinMode(s_5V_pin, INPUT_PULLUP);          //s_5V Sensor Supply Voltage           A0
pinMode(EOP_pin, INPUT_PULLUP);           //Engine Oil Pressure                  A1
pinMode(ECT_pin, INPUT);                  //Engine Coolant Temperature TD5 NTC   A2  no PULLUP here, parallel with TD5 ECU, pullup is there.
pinMode(TDP_pin, INPUT_PULLUP);           //Turbo Differential Pressure          A3
pinMode(ECL_pin, INPUT_PULLUP);           //ECL Engine Coolant Level             A4

pinMode(EOP_pin, INPUT_PULLUP);           //EOP Engine Oil Pressure             D10

pinMode(EmCut_pin, OUTPUT);               //Fuel Pump Relay inhibit             D12
pinMode(redLED_pin, OUTPUT);              //onboard red LED                     D13

//DS18B20 for EOT Engine Oil Temperature
DS_EOT.begin();
delay(300);
DS_EOT.getAddress(DS_EOT_address, 0);            //Get the physical address from the DS18B20 from 0th device on the bus
DS_EOT.setResolution(DS_EOT_address, 9);         //9 bit resolution for temperature... sufficient and much quicker

}


/************************************************************************/
/*                             L O O P                                  */
/************************************************************************/
void loop()
{
                                                //first check if its been long enough since last data, so as to now go get sensor data:
if (millis() > TC_time + TC_interval)            //if more that TC_interval has passed since last data, get them now.
  {
   get_Sensor_data();                           //go fill up on sensor data
  }

SensorData_Parser();                             //and sort the data into Units of measure etc


//Check sensor data for ALARMs and CRITICALs

//CAN-bus OBD stuff
//check for incoming message calling for Data. CAN and OBD is Pull Only data


//check incoming obd can message
if (CAN_MSGAVAIL == CAN.checkReceive())
{
read_CAN_msg();
}

if (!which_can_msg)
{
CAN_msg_response();           //respond to message received
}

//do the LED
// Flash the LED - fast or slow.   redLEDfast == 1 is fast, 0 is slow
// check if it is time to change led state
if (redLEDfast)
{
if ((millis()-redLEDtimer) >= redLEDFinterval)
   {
   // It is time to change state. Calculate next state.
   if (redLEDon == LOW)
      {
      redLEDon = HIGH;
      }
      else
      {
      redLEDon = LOW ;
      }
   // Write new state
   digitalWrite (redLED_pin, redLEDon);
   // Reset timer
   redLEDtimer = millis();
   }
   else
   {
   if (!(redLEDfast))
      {
      if (millis()-redLEDtimer >= redLEDSinterval)
         {
          if (redLEDon == LOW)
             {
             redLEDon = HIGH ;
             }
             else
             {
             redLEDon = LOW ;
             digitalWrite(redLED_pin, redLEDon);
             redLEDtimer = millis();
             }
         }
       }
   }
}
}          //END of     L  O  O  P


/************************************************************************/
/*                          C A N - b u s                               */
/************************************************************************/

void read_CAN_msg()
{
CAN.readMsgBuf(&len, rxd_message);
canID = CAN.getCanId();
for (i=0; i<len; i++)
{
BuildMessage = BuildMessage + rxd_message[i] + ",";
}

 //Check which message was received.
 //Respond with Sensor Data
       if(BuildMessage=="2,1,0,0,0,0,0,0,") {which_can_msg = 0;}  //PID = 0x00, supportedPID
       //{CAN.sendMsgBuf(0x7E8, 0, 8, 0x00, SupportedPID);}
       

       if(BuildMessage=="2,1,0x6B,0,0,0,0,0,") {which_can_msg = 2;}  //PID = 0x6B, EGT
       //{CAN.sendMsgBuf(0x7E8, 0, 7, 0x6B, EGT);}

       if(BuildMessage=="2,1,0x84,0,0,0,0,0,") {which_can_msg = 3;}  //PID = 0x84, CHT
       //{CAN.sendMsgBuf(0x7E8, 0, 7, 0x84, CHT);}

       if(BuildMessage=="2,1,0x5C,0,0,0,0,0,") {which_can_msg = 4;}  //PID = 0x5C, EOT
       //{CAN.sendMsgBuf(0x7E8, 0, 7, 0x5c, EOT);}

       if(BuildMessage=="2,1,0x0B,0,0,0,0,0,") {which_can_msg = 5;}  //PID = 0x0B, EOT
       //{CAN.sendMsgBuf(0x7E8, 0, 7, 0x0B, EOP);}

       if(BuildMessage=="2,1,0x05,0,0,0,0,0,") {which_can_msg = 6;}  //PID = 0x05, ECT
       //{CAN.sendMsgBuf(0x7E8, 0, 7, 0x05, ECT);}

       if(BuildMessage=="2,1,0x6F,0,0,0,0,0,") {which_can_msg = 7;}  //PID = 0x6F, TDP
       //{CAN.sendMsgBuf(0x7E8, 0, 7, 0x6F, TDP);}

       if(BuildMessage=="2,1,0x1E,0,0,0,0,0,") {which_can_msg = 8;}  //PID = 0x1E, ECL
       //{CAN.sendMsgBuf(0x7E8, 0, 7, 0x1E, ECL);}

/*      if(BuildMessage=="2,1,61,0,0,0,0,0,"){CAN.sendMsgBuf(0x7E8, 0, 7, CAT2Temp);}
       if(BuildMessage=="2,1,62,0,0,0,0,0,"){CAN.sendMsgBuf(0x7E8, 0, 7, CAT3Temp);}
       if(BuildMessage=="2,1,63,0,0,0,0,0,"){CAN.sendMsgBuf(0x7E8, 0, 7, CAT4Temp);}
*/
BuildMessage="";
}

void CAN_msg_response()
{

switch (which_can_msg)
{
case 1:
//CAN.sendMsgBuf(0x7E8, 0,   7, 0x6B, sensor);
//                 id  frmt len pid data1  2  3   4  
//               0x7E8 0x41 pid data1   2    3    4

break;

case 2:
//PID_EGT 6B
CAN.sendMsgBuf(0x7E8, 0, PID_EGT, EGT);
break;
}
/*
EGT 6B
CHT 84
EOT 5C
EOP 0B
ECT 05
TDP 6F
ECL 1E
*/


}


/************************************************************************/
/*                           S E N S O R S                              */
/************************************************************************/
void get_Sensor_data()
{

s_5V_data = analogRead(s_5V_pin);


/*******Thermocouples*********
          MAX6675
*****************************/

//read thermocouples but MAX6675 note they take 200ms to do a read cycle.
EGT_data = TC_EGT.readCelsius();                  //read current actual temperatures at the exhaust gas thermocouple
   
   CHT_data = TC_CHT.readCelsius();                  //Cylinder Head Temperature thermocouple

/*****************************
           N T C
*****************************/
   int i;
   float average;
      for (i=0; i<numsamples; i++)
          {
          samples[i] = analogRead(ECT_pin);                   //read current actual temperatures at the coolant thermocouple - will be off the LR TD5 real sensor
          delay(10);
          }
      average = 0;  
      for (i=0; i<numsamples; i++)
          {
          average += samples[i];
          }
   average /= numsamples;                            //average is the mean of the numsamples (5) samples read from the ntc.
   //convert this adc mean value to the a mean resistance of the ntc, dependent on its temperature:

   float steinhart;                                  //nerdy bloke who came up with relationship between ntc resitance and temperature
       steinhart = average / NTCohms;                    // R/Ro
       steinhart = log(steinhart);
       steinhart /= ntcBcoefficient;                     // 1/beta * ln(R/Ro)
       steinhart += 1.0 / (NTCnominaldegC + 273.15);     // + (1/To)
       steinhart = 1.0 / steinhart;                      // reciprocal, float
                                                     //steinhart is now engine coolant tempurature in Kelvin
   ECT = ECT - 273.15;                                    // kelvin to degC


/****************************
   Universal Oil Pressure
****************************/
   EOP_data = analogRead(EOP_pin);


/****************************
   DS18B20 Oil Temperature
****************************/
   DS_EOT.requestTemperatures();                     //this one tells (all) the Dallas Temperatures to assimilate (convert) their internal sensor
                                                     //      data to readable temperature, noted on the Dallas devices' "scratchpad"

/****************************
     Coolant Level Switch
****************************/
   ECL = digitalRead(ECL_pin);                      //digitalread on A4
 
/****************************
    time stamp this round
****************************/
TC_time = millis();                                   //(re-)set the interval timer for the get_data cycle.

}

void SensorData_Parser()
{                                                    //     s_5V  EGT  CHT  ECT  EOP  ECL  TDP  EOT
                                                     //pins: A4    6    7    A    A0   A1   A2  D10
                                                     //      dir  TC1  TC2  NTC  MCP  Reed MCP  I2C
                                                      

//Take raw data from the Sensors and convert it to usable data.

s_5V     = (s_5V_data*(s_5V/1023));                        //puts s_5V into units of Volt, from ADC

EGT      = round(EGT_data);                                //EGT now has exhaust gas temperature in degC integer.

CHT      = round(CHT_data);                                //CHT now has cylinder head temperature in degC integer.

//ECT is done in getSensor, cos its best averaging several samples to reduce noise.

//TDP                                                      MXP4425 pressure sensor on analog input
TDP      = (TDP_data * (s_5V/1023));                       //Volts, proportional to pressure
TDP      = ((TDP/s_5V)-0.04)/0.00369/100;                  //P = ((Vout/Vss)-0.04)/0.00369 kPa /100 Bar
TDP      = round((TDP*10)/10.0);                           //eliminate shed loads of (un-)precision.
                     
EOP      = map(EOP_data, 0, 1023, 0, 4);                   //Bar, universal pressure sensor on analog input, 0 - 4Bar = 0 - 1023ADC

EOT_data = DS_EOT.getTempC(DS_EOT_address);                //reads last assimilated temperature from the DS18B20 scratchpad to EOT_data
EOT      = round(EOT_data);                                //EOT now has engine oil temperature in degC integer.
}




//Scan Tool reader (Android Torque) must send us a message asking for the data associated with
//a PID, we read the message, and associate the request with a PID for which we have data.
//Mode 1 is for any actual current data (and it is dynamic)
//Mode 2 is for recorded Trouble Codes, for devices that have *previously* provoked memorisation of data due to out of range PID value
//Mode 3 is for clear Device Trouble Codes.. and would mean there is no Mode 2 data recorded

//so we only need Mode 2 comms.
//the Reader sends us a message containing the PID and we respond with the PID and its value
//the Reader sends us an 8 byte message:
//byte 1     2      3    4     5     6    7    8   
//   mode  format  PID  byte4 . . . .        byte8
//the PID is one byte
//the data for a PID, its value, is between 1 and 4 bytes, dependent on value range.
//
//Mode 1 PID 00 is special and if sent by the Reader, requires the ECU (us) to return a meesage to it
//listing all the PID's supported. So later the Reader will need send to only us a PID for which we
//have data; other ECU's may be on the bus and will not respond to a message sent to another ECU.
//They also won't respond to an enquiry for which they are not responsible.
//the Reader sends messages (optionally) with source and target addresses, so a Mode 1 PID 00 message will
//be addressed appropriately.


/*

Back to basics to get CAN-message builder working with int byte analogRead()'s

TD5_Engine_Sensors was getting too heavy with old stuff, AND
eventually, i targeted this program for the Arduino Uno.. not Due - big change from 32bit back to 16
and this now compiles, though loads still to do before its nearly functional. (not important but: Previously,
with Visual Micro i hadn't used the external Arduino IDE specifically so i had only the Due board available.
Any more is a separate driver install manually for each (i think). After other problems provoked installation
of Arduino IDE 1.8 (still in AS VM) i had the choice of boards without me having to go and get drivers.

With the Due as target, the "can't convert uint8_t (aka unsigned char*) as byte" wouldn't go away whatever i did -
and while i didn't know what and why, it seems the libraries' don't have direct equivalence between
the two, indeed, #includes for cstio and iostream caused no direct problem at compile time for the _Due_
but, later,  for the Uno they cause the compiler to fail and terminate; I think i might have expected that
to be the other way round (cause failure for the Due not the Uno) but i imagine that says less about the (compile)
process and more about my ack of understanding of the process!

Do you suppose or know if the scanf and sprintf calls as here will extract from the incoming message
and create (format) the send message with the PID and structure required for CAN OBD? That is to say, in principle,
is it reasonable to believe this will work?
*/

/************************************************************************/
/*                             I N C L U D E S                          */
/************************************************************************/

 #include <max6675.h>                         //Thermocouple devices on SPI
 #include <OneWire.h>                         //I2C
 #include <DallasTemperature.h>               //DS18B20 temperature sensor on OneWire I2C
 #include <mcp_can.h>                         //CAN-bus shield, MCP2525 CAN-bus controller


/************************************************************************/
/*                            V A R I A B L E S                         */
/************************************************************************/

 uint16_t   i                 =    0;        //generic for.to loop counter mark
 uint16_t   data_16bit        =    0;        //data label for derived sensor values
                         //will be staging point for sensor data before separating bytes ready for bundling

// EGT Pin D6, PID = 0x6B, Max = 800ºC, data = 2byte
 uint8_t    EGT_pin           =    6;
 uint8_t    PID_EGT           = 0x6B;
 uint16_t   EGT_16bit         =    0;

// CHT PIN D7, PID = 0x84, Max = 250ºC, data = 1byte
 uint8_t    CHT_pin       =    7;
 uint8_t    PID_CHT           = 0x84;
 uint8_t    CHT               =    0;

// ECT PIN A2, PID = 0x05, Max = 120ºC, data = 1btye
 uint8_t    ECT_pin           =   A2;
 uint8_t    PID_ECT           = 0x05;
 uint8_t    ECT               =    0;

//CAN messages
 uint8_t    CAN_rx_msg[8];                         //composite CAN message received from CAN shield
 int8_t     CAN_send_msg[8];                       //composite CAN message prepared ready to send by CAN shield
 bool       CAN_msg_sent      =false;

 int32_t    msg_data[5];                           //bytes derived from sensor data, value of the PID, to send by CAN
                                                   //[0] and [1] will be filled with sensor data for sending
 uint8_t    len               =    0;              //check length of incoming can message

 uint8_t    send_PID          =    0;              //while send_PID is not zero, from loop, build a can message and send it.

 uint32_t   TC_time           =    0;              //thermocouples take up to 200ms to read, don't read them while they're busy or they'll never respond
 uint32_t   TC_interval       =  300;              //marker for when TC were last sent do their internal to update actual temperature from physical thermocouple



/************************************************************************/
/*                             D E V I C E S                            */
/************************************************************************/

//CAN_bus Shield, MCP-can
 uint8_t CAN_SPI_CSpin             = 9;             //CAN-bus Shield SPI CS, pin 9 determined by hardware *SHIELD*

//TC_EGT Thermocouple MAX6675_1
 uint8_t TC_CLK                    = 4;             //SPI bus Clock common
 uint8_t TC_DO                     = 5;             //SPI bus Data Out common
 uint8_t TC_EGT_CS                 = 6;             //SPI EGT specific Chip Select

//TC_CHT Thermocouple MAX6675_2
 uint8_t TC_CHT_CS                 = 7;             //SPI CHT specific CS  


/************************************************************************/
/*                   D E V I C E    I N S T A N C E S                   */
/************************************************************************/

//Dallas Temperature
 DeviceAddress DS_EOT_address;                  //Dallas Temperature

//Thermocouples
//digital pins:    4        6,7       5
 MAX6675 TC_EGT(TC_CLK, TC_EGT_CS, TC_DO);      //Exhaust Gas Temp       CS = 6
 MAX6675 TC_CHT(TC_CLK, TC_CHT_CS, TC_DO);      //Cylinder Head Temp     CS = 7
 
//CAN-bus SHIELD, MCP2515
 MCP_CAN CAN(CAN_SPI_CSpin);                    //CAN_bus Shield service CS = 9


/************************************************************************/
/*                               S E T U P                              */
/************************************************************************/

void setup()
{
//pinModes(I/O)
//TC's on SPI, no pinModes, Chip Selects on D6 and D7, CLK D4, Do D5.
//CAN-shield SPI, no pinMode, CS on 9 (Shield hardware), MOSI and MISO board dependent per SPI wire library
//Dallas DS18B20 on I2C, no pinModes. Data on D10
//s_5V A0, ECT A1, EOP A2, TDP A3, ECL A4  //inputs, ECT has no Pullup, ECL is digitalread on Analog
//LED D13, EmCut D12                       //outputs

 pinMode(13, OUTPUT);                     //red LED TODO integrate LED into formal sections
                                          //and TODO implement fast and slow flash for status
 CAN.begin(CAN_500KBPS);
 if (!CAN_OK)
    {
    digitalWrite(13, HIGH);
    }

//TODO must be more to come here?
}



/************************************************************************/
/*                                 L O O P                              */
/************************************************************************/
void loop()
{
 if (millis() > TC_time + TC_interval)
    {
    read_sensors();
    }
 
 if (CAN_MSGAVAIL == CAN.checkReceive())
    {
    read_CAN_msg();
    }


//TODO more functional stuff
//
 
}

/************************************************************************/
/*                    R E A D  message  from  C A N                     */
/************************************************************************/
void read_CAN_msg()
{
 CAN.readMsgBuf(&len, CAN_rx_msg);


//Assimilate incoming CAN message
//TODO verify difference between Ext and Size (number of (extra) bytes in Data) bytes.
//        //rxd-msg         ID             ext           mode           PID         data1   2      3      4
//        //               0x7DF            0            0x01           0x06          0     0      0      0...  data bytes maybe 55, 55, 55, 55.
 scanf(CAN_rx_msg,"        %3x,           %2x,          %1x,           %2x,        *2x,   *2x,   *2x,   *2x",
                       CAN_rx_msg[0], CAN_rx_msg[1], CAN_rx_msg[2], CAN_rx_msg[3]);

//validate the incoming PID, we'll ignore everything else (no one cares if we send it and no one's listening!
 if (CAN_rx_msg[3])
    {
    send_PID        = CAN_rx_msg[3];
    }
   else
    {
    send_PID        = 0;
    };

//return from here with CAN_rx_msg has fixed bytes set and send_PID validated and defined to determine which sensor values to send in
}


/************************************************************************/
/*                                Ts & Ps                               */
/************************************************************************/
void read_sensors()
{
//Temperatures
// . . . analogRead's and send thermocouple internal "convert" commands
//                    and after, read thermocouple scratchpad for temperatures
/*
 pseudo code
 EGT = read.scratchpad;      #one thermocouple MAX6675 TC_EGT
 EGT = map; round; etc
 uint16_t EGT_16bit = EGT;

 CHT = read.scratchpad;      #two thermocouple TC_CHT
 CHT = round; map; etc
 (CHT is uint8_t)

 ECT = analogRead(ECT_pin),
 ECT = mean; map;            NTC thermistor is noisey (+= very responsive, sensitve), average of five samples.
 /ECT is uint8_t
*/

//Pressures
//Levels

//Once we've read a sensor
//16 bit data, derive two bytes
 msg_data[0] = (EGT_16bit >> 8 & 0x00ff);    EGT_16bit low byte                   } or high byte
 msg_data[1] = (EGT_16bit & 0x00ff);         EGT_16bit high byte (or multiplier=  } or low byte, not good at bit wise. I'm alright at banging though.

//if its 1 byte
 msg_data[0] = (CHT);

}


/************************************************************************/
/*                     C A N  message  B U I L D E R                    */
/************************************************************************/
void build_CAN_Message()
{
//format output CAN_send_msg as string of HEX data
//TODO verify difference between Ext and Size (number of (extra) bytes in Data) bytes.
//        //send_msg[8]    ID  ext/size mode  PID         Data1         data2        data3        data4
//        //              0x7E8  0x00   0x41  0x06      val low byte   high byte     0x00         0x00
 sprintf(CAN_send_msg, " %X03,  %X02,  %X02, %X02,       %02X,         %X02,        %X02,        %X02",
                         0x7E8, 0x00,  0x41, send_PID,  msg_data[0],  msg_data[1], msg_data[2], msg_data[3]);

//more to go here
}



/************************************************************************/
/*                        nothing functional                            */
/************************************************************************/
/* thoughts and prompts
 CAN message comes in with 0x7DF ID, so its a request, and the PID in that message will determine what data is sent in the reply msg
 Reply to rx'd message with 0x7E8 as ID, the same PID definition plus four more Bytes, of which #1 (and #2) will be the value of the
 PID, value is derived from sensor data.
 more, the 2nd byte in the messages, which will be always 0, indicates the frame is normal and not 'extended'.

rxd_msg:
  ID    ext  mode   PID    Data1     2         3         4
 0x7DF   0   0x01   0x06     0       0         0         0
         ^
??Ext or Size is normal/extended frame (11- 29-bit ID) or number of (applicable/appropriate) (extra) bytes in Data. When? How?
but regardless we only need to know that it comes in as one byte only.. so skip that one, ignore it.

send_msg:
  ID    ext  mode   PID    Data1     2         3         4
 0x7E8,  0,  0x41,  0x06,   val,    val2,      0,        3
         ^
         11/29 bit ID OR 1, 2, 3 or 4 valid bytes for Data

We need only read the PID byte from the rxd_msg and ignore all else incoming:
If the incoming rxd_msg PID is valid, then note what it is and respond accordingly.
To build the send_msg, PID stays the same as rxd, and the data value, if 16 bit int needs
dividing into the two data bytes. That's the pinch! Else, Data1 is one byte of sensor Data.

read rxd-msg
if (rxd_pid)
  send_pid = rxd_pid;
else
  rxd_pid = 0;
  switch case, msg_data = data from respective sensor
  data1 = first byte of sensor value
  data2 = second byte of sensor value  (according to OBD FORMULA ref for A and B bytes).
send_msg = {07E8, 0, 0x41, send_pid, Data1, Data2, 0, 0}
                 ^ means no (0) extra bytes in Data, one (1) only, Data2 is null?
*/



/*
        T D 5   E n g i n e    S e n s o r s

to read critical temperatures and pressures and send to CAN-bus shield for ELM327 and then Android display.
Respond to out-of-limit Ts & DPs in Arduino: Out-of-Limit (OOL) parameters will cause Advisory, Alarm, Imminent or "Critical engine off".

Advisory: visual note on display (and beep?)
Alarm: long audio siren and "Indication"
Imminent: Alarm, plus second alarm and further Critical if further deterioration of any (other) sensor data
Critical: Engine OFF without further warning and

Parameters:
                             Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti   sensor  Pin    Mnemonic
Sensor reference/supply 5V      s_5V      V        4.5     5.0     5.2     5.3     4.75                     analog  A0      s_5V
Exhaust Gas Temperature         EGT      �C        200     450     700     600     650     670      800     MAX1spi D6      EGT
Cylinder Head Temperature       CHT      �C        50      110     180     170     180     180      230     MAX2spi D7      CHT
Engine Oil Temperature          EOT      �C        30       80     100      90      95     100      115     DS18B20 D10     EOT
Engine Oil Pressure             EOP      Bar       0.5     1-3      4      1.5      1      0.8      0.7     OPS     A1      EOP
Engine Coolant Temperature      ECT      �C        30       75      95      90      95     99       102     NTC     A2      ECT   
Turbo Differential Pressure     TDP      Bar        0       0       10                                      MPX     A3      TDP
Engine Coolant Level            ECL      Arb        1       4       5       2       1      0.5      0.3     switch  A4      ECL

//SPI: CLK CS DATA
//I2C: 1-wire DATA

MAX6675    SPI thermocouple interface, direct output in �C. 200ms read cycle time. EGT and CHT
DS18B20    I2C temperature sensor
Univeral Pressure sensor - car type 10 - 180ohm for oil pressure 0 - 250kPa, 100kPa = 1Bar
NTC temperature sensor 10kohm, 1k ~30�C, LR TD5 original ECT paralleled to ECU, no PULLUP (there is one, provided by ECU)
MPX4250DP  proportional analog output pressure sensor (DP version is differential with vacuum-side port)
coolant level sensor reed float switch mounted in wall of Expansion Tank.

MCP2515    CAN-bus shield will take our Arduino sensor data and shake it up into OBD formated data, MCP runs SPI, CS on pin D9
EmCut      Relay to disable fuel pump in Critical condition (eg. No oil pressure)
*/      

/************************************************************************/
/*                             I N C L U D E S                          */
/************************************************************************/

#include <max6675.h>              //Thermocouple devices on SPI
#include <OneWire.h>              //I2C
#include <DallasTemperature.h>    //DS18B20 temperature sensor on OneWire I2C
#include <mcp_can.h>              //CAN-bus shield, MCP2525 CAN-bus controller


/************************************************************************/
/*                   P R O G R A M    V A R I A B L E S                 */
/************************************************************************/
int i;                       //loop counter

//pin A0                                   Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti
//Sensor reference/supply 5V                 5Vss      V        4.5     5.0     5.2     5.3     4.75                   
float s_5V_data  = 0.0;      //from Analog input direct ADC result, maths ADC to Volts. pin A0.
float s_5V       = 5.0;      //resulting real s_5V  V O l T A G E, needed to measure the s_5V, so start with 5.0
float s_5Vmin    = 4.5;      
float s_5Vmax    = 5.1;
float s_5Vadv    = 5.2;
float s_5Valm    = 4.7;
//s_5V is used in formulas for parsing sensor data where sensor Vss is required, eg EOP and TDP.

//pin D6                                   Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti
//Exhaust Gas Temperature                    EGT       �C       200     450     700     600     650     670      800  
int   EGT_data     = 0;      //from sensor, data from MAX6675 I2C thermocouple reader CS = pin D6
int   EGT          = 0;      //resulting real EGT    T E M P E R A T U R E ,   �C
int   EGTmin       = 200;
int   EGTmax       = 700;
int   EGTadv       = 600;
int   EGTalm       = 650;
int   EGTimm       = 670;
int   EGT_critical = 800;
byte  EGT_flag     = 0;      //extra data for result of Stutas Analysis, resulting in alarm or EmCut. Byte for Andorid

//pin D7                                   Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti
//Cylinder Head Temperature                  CHT      �C        50      130     180     170     180     190      230
int   CHT_data     = 0;      //from sensor, data from MAX6675 I2C thermocouple reader CS = pin D7
int   CHT          = 0;      //resulting real CHT    T E M P E R A T U R E ,   �c
int   CHTmin       = 50;
int   CHTmax       = 180;
int   CHTadv       = 170;
int   CHTalm       = 180;
int   CHTimm       = 190;
byte  CHT_critical = 230;
byte  CHT_flag     = 0;

//pin D10                                   Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti
//Engine Oil Temperature                     ECT      �C        30       80     100      90      95     100       115  
float EOT_data   = 0;      //from DS18B20 I2C sensor, I2C 1wire DATA on pin D10
byte  EOT          = 0;      //resulting real ECT   T E M P E R A T U R E ,   �c
int   EOTmin       = 30;
int   EOTmax       = 100;
int   EOTadv       = 90;
int   EOTalm       = 95;
int   EOTimm       = 100;
byte  EOT_critical = 115;
byte  EOT_flag     = 0;

//pin A1                                   Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti  
//Engine Oil Pressure                        EOP      Bar       0.5     1-3      4      1.5      1      0.8      0.7  
float EOP_data     = 0.0;  //from sensor, ADC result, analog 0 - 1023 analog ADC pin A1
float EOP          = 0.0;  //resulting real EOP   P R E S S U R E ,    Bar units 1BAR = 100kPa
float EOPmin       = 0.5;
float EOPmax       = 4.0;
float EOPadv       = 1.5;
float EOPalm       = 1.0;
float EOPimm       = 0.8;
float EOP_critical = 0.3;
byte  EPO_flag     = 0;

//pin A2                                       Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti
//Engine Coolant Temperature                     ECT      �C        30       80      95      90      95     99       102   
int   ECT_data     = 0;      //from analogue sensor, original Land Rover TD5 sensor, ntc on pin A2
byte  ECT          = 0;      //resulting real ECT   T E M P E R A T U R E ,   �c
int   ECTmin       = 30;
int   ECTmax       = 95;
int   ECTadv       = 90;
int   ECTalm       = 95;
int   ECTimm       = 99;
byte  ECT_critical = 102;
byte  ECT_flag     = 0;

//pin A3                                    Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti
//Turbo Differential Pressure                 Bar        0       0       2       4               3                     
int   TDP_data     = 0;      //from analogue MCP4250DP sensor, ADC result on pin A3
byte  TDP          = 0;      //resulting real TDP   P R E S S U R E ,   Bar
int   TDPmin       = 0;
int   TDPmax       = 4;
byte  TDP_critical = 3;
byte  TDP_flag     = 0;

//pin A4                                   Mnemonic   UNIT      min     nom     max     Adv    Alarm    Imm     Criti
//Engine Coolant Level                       ECL      Arb        1       4       5       2       1      0.5      0.3  
//reed level float switch on expansion tank to detect low level coolant
byte  ECL          = 0;  //from low level switch                  digital read on analog pin A4
byte  ECL_critical = 0;  //check if reed is NO with good coolant level
byte  ECL_flag     = 0;


//MAX6676 considerations                //if MAX6675 is polled too frequently, result is it never answers at all.
                                       //   .. each poll starts the "read" process so, ask again and it never answers before it starts over.
int TC_interval         = 300;          //limit number of reads required from MAX6675. Max 5 per second = one every 200ms.
int TC_time;                            //by timing between each read cycle.

//MPX4250DP considerations              //MPX device measure pressure over range 0 - 250kPa (0 - 2.5 bar) over 0 - 5V full range output.
int MPX_TDP             = 0;            //initial data read from analogue MCP4250 pressure sensor.

//CANbus OBD shield and stuff
//int CAN_SPI_CSpin       = 9;
byte canID                = 0x00;         //not sure why we want a CAN id and a CAN tx id... or even if .
int txID                  = 0x07E8;       //id for our new tx ECU. This is also the address to which
                                         //polling will be sent by the scan tool / Android Torque. I THINK.
byte len                  = 0;
byte msgBuffer[8];                        //incoming messages, soliciting data received here  
String BuildMessage       = "";
int MSGIdentifier         = 0;


/* example CAN-bus OBD message:
CAN.sendMsgBuf(INT32U txID, INT8U ext, INT8U len, INT8U *buf)
txID is the CAN-bus address.
ext '0' means standard frame. '1' means extended frame.
len represents the length number of bytes in this frame.
buf is the data-content of this message.

CAN.sendMsgBuf(txID, 0, len, OBDmsg)
OBDmsg is the parsed data from the sensor in the format:
[PID, data, status]
OBD says the data may be more than one byte but the number of bytes in len, must correspond.
Our PID�s are all one byte so len will be 3. (its not an array, so not n-bytes + 1).
*/

//CAN-bus OBD PID's:
//Mode01:
byte PID_EGT                  = 0x6B;          //Exhaust Gas Temperature     (Exhaust Gas Recirculation Temperature)
byte PID_CHT                  = 0x84;          //Cylinder Head Temperature   (Manifold Surface Temperature)
byte PID_EOT                  = 0x5C;          //Engine Oil Temperature
byte PID_EOP                  = 0x0B;          //Engine Oil Pressure          (Inlet Manifold Absolute Pressure)
byte PID_ECT                  = 0x05;          //Formula A-40
byte PID_TDP                  = 0x6F;          //Turbo Differential Pressure (Turbo Compressor Inlet Pressure)
byte PID_ECL                  = 0x1E;          //Engine Coolant Level alarm  (Auxiliary Input Status, PTO)      

byte PID_supported[8]         = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; //...if we're asked which PID wwe support, its all of them!
byte PID_MILcleared[7]        = {4, 65, 63, 34, 224, 185, 147};

//CAN Messages
byte rxd_message[8];
byte send_message[8];
int which_can_msg             = 0;             //identifer for us, dependent on CAN message recieved (and its PID)
byte EGTmsg[8]                   = {4, 65, PID_EGT, EGT, EGT_flag};

byte CHTmsg[7]                   = {4, 65, PID_CHT, CHT, 0, 185, 147};
/*
byte EOTmsg[]                   = {4, 65, PID_EOT, EOT, EOT_critical};
byte EOPmsg[]                   = {4, 65, PID_EOP, EOP, EOP_critical};
byte ECTmsg[]                   = {4, 65, PID_ACT, ECT, ECT_critical};
byte TDPmsg[]                   = {4, 65, PID_TDP, TDP, TDP_critical};
byte ECLmsg[]                   = {4, 65, PID_ECL, ECL, ECL_critical};
*/

//NTC considerations:
int NTCohms                   = 10000;         //resitance value of NTC at nominal degC
int NTCnominaldegC            = 25;
int numsamples                = 5;             //NTC is noisey, so take the mean of (5) samples
int ntcBcoefficient           = 3950;          //used in NTC maths, arbitrary really, Beta coef for the TD5's NTC coolant temp sensor is not available.
int SeriesRohms               = 10000;         //assume TD5 ECU uses 10K series with the 10K NTC
uint16_t samples[5];                           //for convenience, the successive sample values will go in this array
                                              //numsamples as variable in array definition requires integer
//LED FLASHER, STATE ENGINE
unsigned long redLEDFinterval = 300;
unsigned long redLEDSinterval = 1000;

unsigned long redLEDtimer     = 0;             //time instance during flash

bool redLEDon                 = 0;
bool redLEDfast               = 0;             //0 = slow, 1 = fast.
bool redLEDflash              = 0;             //0 = static, 1 = flash.

/************************************************************************/
/*                             D E V I C E S                            */
/************************************************************************/

//Create and connect to DEVICES
//                    H A R D W A R E    P I N    N U M B E R S
int s_5V_pin                  = A0;            //voltage, analog ADC input
int EOP_pin                   = A1;            //OPS, Oil Pressure Sensor
int ECT_pin                   = A2;            //LR TD5 coolant temperature NTC
int TDP_pin                   = A3;            //NXP MPX4250DP pressure sensor, analog ADC input
int ECL_pin                   = A4;            //switch: reed switch, digital input

//TC_EGT Thermocouple MAX6675_1
int TC_CLK                    = 4;             //SPI bus Clock common
int TC_DO                     = 5;             //SPI bus Data Out common
int TC_EGT_CS                 = 6;             //SPI EGT specific Chip Select

//TC_CHT Thermocouple MAX6675_2
int TC_CHT_CS                 = 7;             //SPI CHT specific CS

int CAN_SPI_CSpin             = 9;             //SPI CAN_bus SHIELD CS

int I2C_data_pin              = 10;            //I2C OneWire Data bus

int EmCut_pin                 = 12;            //Output to relay to Emergency Cut, fuel pump off, *** ENGINE STOP ***

int redLED_pin                = 13;            //on-board red LED

/*
                  ***** E N G I N E   S T O P   if *****
              1) Oil Pressure below critical
         or   2) CHT Cylinder Head Temperature above CRITICAL
         or   3) ECT Engine Coolant Temperature above CRITICAL
         or   4) EGT Exhaust Gas Temperature and ECT both above ALARM

                        *************************
*/

/************************************************************************/
/*                   D E V I C E    I N S T A N C E S                   */
/************************************************************************/

//Dallas Temperature
DeviceAddress DS_EOT_address;                  //Dallas Temperature

//Thermocouples
//digital pins:  4        6,7       5          
MAX6675 TC_EGT(TC_CLK, TC_EGT_CS, TC_DO);      //Exhaust Gas Temp       CS = 7
MAX6675 TC_CHT(TC_CLK, TC_CHT_CS, TC_DO);      //Cylinder Head Temp     CS = 6

MCP_CAN CAN(CAN_SPI_CSpin);                    //CAN_bus Shield service CS = 9

OneWire I2C(I2C_data_pin);                     //I2C OneWire service  data = 10

DallasTemperature DS_EOT(&I2C);                //Dallas Temperature instigated for EOT on OneWire bus


/************************************************************************/
/*                               S E T U P                              */
/************************************************************************/
void setup()
{
//Serial.begin(38400);
if (CAN_OK == CAN.begin(CAN_500KBPS))
  {
  }

pinMode(s_5V_pin, INPUT_PULLUP);          //s_5V Sensor Supply Voltage           A0
pinMode(EOP_pin, INPUT_PULLUP);           //Engine Oil Pressure                  A1
pinMode(ECT_pin, INPUT);                  //Engine Coolant Temperature TD5 NTC   A2  no PULLUP here, parallel with TD5 ECU, pullup is there.
pinMode(TDP_pin, INPUT_PULLUP);           //Turbo Differential Pressure          A3
pinMode(ECL_pin, INPUT_PULLUP);           //ECL Engine Coolant Level             A4

pinMode(EOP_pin, INPUT_PULLUP);           //EOP Engine Oil Pressure             D10

pinMode(EmCut_pin, OUTPUT);               //Fuel Pump Relay inhibit             D12
pinMode(redLED_pin, OUTPUT);              //onboard red LED                     D13

//DS18B20 for EOT Engine Oil Temperature
DS_EOT.begin();
delay(300);
DS_EOT.getAddress(DS_EOT_address, 0);            //Get the physical address from the DS18B20 from 0th device on the bus
DS_EOT.setResolution(DS_EOT_address, 9);         //9 bit resolution for temperature... sufficient and much quicker

}


/************************************************************************/
/*                             L O O P                                  */
/************************************************************************/
void loop()
{
                                                //first check if its been long enough since last data, so as to now go get sensor data:
if (millis() > TC_time + TC_interval)            //if more that TC_interval has passed since last data, get them now.
  {
   get_Sensor_data();                           //go fill up on sensor data
  }

SensorData_Parser();                             //and sort the data into Units of measure etc


//Check sensor data for ALARMs and CRITICALs

//CAN-bus OBD stuff
//check for incoming message calling for Data. CAN and OBD is Pull Only data


//check incoming obd can message
if (CAN_MSGAVAIL == CAN.checkReceive())
{
read_CAN_msg();
}

if (!which_can_msg)
{
CAN_msg_response();           //respond to message received
}

//do the LED
// Flash the LED - fast or slow.   redLEDfast == 1 is fast, 0 is slow
// check if it is time to change led state
if (redLEDfast)
{
if ((millis()-redLEDtimer) >= redLEDFinterval)
   {
   // It is time to change state. Calculate next state.
   if (redLEDon == LOW)
      {
      redLEDon = HIGH;
      }
      else
      {
      redLEDon = LOW ;
      }
   // Write new state
   digitalWrite (redLED_pin, redLEDon);
   // Reset timer
   redLEDtimer = millis();
   }
   else
   {
   if (!(redLEDfast))
      {
      if (millis()-redLEDtimer >= redLEDSinterval)
         {
          if (redLEDon == LOW)
             {
             redLEDon = HIGH ;
             }
             else
             {
             redLEDon = LOW ;
             digitalWrite(redLED_pin, redLEDon);
             redLEDtimer = millis();
             }
         }
       }
   }
}
}          //END of     L  O  O  P


/************************************************************************/
/*                          C A N - b u s                               */
/************************************************************************/

void read_CAN_msg()
{
CAN.readMsgBuf(&len, rxd_message);
canID = CAN.getCanId();
for (i=0; i<len; i++)
{
BuildMessage = BuildMessage + rxd_message[i] + ",";
}

 //Check which message was received.
 //Respond with Sensor Data
       if(BuildMessage=="2,1,0,0,0,0,0,0,") {which_can_msg = 0;}  //PID = 0x00, supportedPID
       //{CAN.sendMsgBuf(0x7E8, 0, 8, 0x00, SupportedPID);}
       

       if(BuildMessage=="2,1,0x6B,0,0,0,0,0,") {which_can_msg = 2;}  //PID = 0x6B, EGT
       //{CAN.sendMsgBuf(0x7E8, 0, 7, 0x6B, EGT);}

       if(BuildMessage=="2,1,0x84,0,0,0,0,0,") {which_can_msg = 3;}  //PID = 0x84, CHT
       //{CAN.sendMsgBuf(0x7E8, 0, 7, 0x84, CHT);}

       if(BuildMessage=="2,1,0x5C,0,0,0,0,0,") {which_can_msg = 4;}  //PID = 0x5C, EOT
       //{CAN.sendMsgBuf(0x7E8, 0, 7, 0x5c, EOT);}

       if(BuildMessage=="2,1,0x0B,0,0,0,0,0,") {which_can_msg = 5;}  //PID = 0x0B, EOT
       //{CAN.sendMsgBuf(0x7E8, 0, 7, 0x0B, EOP);}

       if(BuildMessage=="2,1,0x05,0,0,0,0,0,") {which_can_msg = 6;}  //PID = 0x05, ECT
       //{CAN.sendMsgBuf(0x7E8, 0, 7, 0x05, ECT);}

       if(BuildMessage=="2,1,0x6F,0,0,0,0,0,") {which_can_msg = 7;}  //PID = 0x6F, TDP
       //{CAN.sendMsgBuf(0x7E8, 0, 7, 0x6F, TDP);}

       if(BuildMessage=="2,1,0x1E,0,0,0,0,0,") {which_can_msg = 8;}  //PID = 0x1E, ECL
       //{CAN.sendMsgBuf(0x7E8, 0, 7, 0x1E, ECL);}

/*      if(BuildMessage=="2,1,61,0,0,0,0,0,"){CAN.sendMsgBuf(0x7E8, 0, 7, CAT2Temp);}
       if(BuildMessage=="2,1,62,0,0,0,0,0,"){CAN.sendMsgBuf(0x7E8, 0, 7, CAT3Temp);}
       if(BuildMessage=="2,1,63,0,0,0,0,0,"){CAN.sendMsgBuf(0x7E8, 0, 7, CAT4Temp);}
*/
BuildMessage="";
}

void CAN_msg_response()
{

switch (which_can_msg)
{
case 1:
//CAN.sendMsgBuf(0x7E8, 0,   7, 0x6B, sensor);
//                 id  frmt len pid data1  2  3   4  
//               0x7E8 0x41 pid data1   2    3    4

break;

case 2:
//PID_EGT 6B
CAN.sendMsgBuf(0x7E8, 0, PID_EGT, EGT);
break;
}
/*
EGT 6B
CHT 84
EOT 5C
EOP 0B
ECT 05
TDP 6F
ECL 1E
*/


}


/************************************************************************/
/*                           S E N S O R S                              */
/************************************************************************/
void get_Sensor_data()
{

s_5V_data = analogRead(s_5V_pin);


/*******Thermocouples*********
          MAX6675
*****************************/

//read thermocouples but MAX6675 note they take 200ms to do a read cycle.
EGT_data = TC_EGT.readCelsius();                  //read current actual temperatures at the exhaust gas thermocouple
   
   CHT_data = TC_CHT.readCelsius();                  //Cylinder Head Temperature thermocouple

/*****************************
           N T C
*****************************/
   int i;
   float average;
      for (i=0; i<numsamples; i++)
          {
          samples[i] = analogRead(ECT_pin);                   //read current actual temperatures at the coolant thermocouple - will be off the LR TD5 real sensor
          delay(10);
          }
      average = 0;  
      for (i=0; i<numsamples; i++)
          {
          average += samples[i];
          }
   average /= numsamples;                            //average is the mean of the numsamples (5) samples read from the ntc.
   //convert this adc mean value to the a mean resistance of the ntc, dependent on its temperature:

   float steinhart;                                  //nerdy bloke who came up with relationship between ntc resitance and temperature
       steinhart = average / NTCohms;                    // R/Ro
       steinhart = log(steinhart);
       steinhart /= ntcBcoefficient;                     // 1/beta * ln(R/Ro)
       steinhart += 1.0 / (NTCnominaldegC + 273.15);     // + (1/To)
       steinhart = 1.0 / steinhart;                      // reciprocal, float
                                                     //steinhart is now engine coolant tempurature in Kelvin
   ECT = ECT - 273.15;                                    // kelvin to degC


/****************************
   Universal Oil Pressure
****************************/
   EOP_data = analogRead(EOP_pin);


/****************************
   DS18B20 Oil Temperature
****************************/
   DS_EOT.requestTemperatures();                     //this one tells (all) the Dallas Temperatures to assimilate (convert) their internal sensor
                                                     //      data to readable temperature, noted on the Dallas devices' "scratchpad"

/****************************
     Coolant Level Switch
****************************/
   ECL = digitalRead(ECL_pin);                      //digitalread on A4
 
/****************************
    time stamp this round
****************************/
TC_time = millis();                                   //(re-)set the interval timer for the get_data cycle.

}

void SensorData_Parser()
{                                                    //     s_5V  EGT  CHT  ECT  EOP  ECL  TDP  EOT
                                                     //pins: A4    6    7    A    A0   A1   A2  D10
                                                     //      dir  TC1  TC2  NTC  MCP  Reed MCP  I2C
                                                      

//Take raw data from the Sensors and convert it to usable data.

s_5V     = (s_5V_data*(s_5V/1023));                        //puts s_5V into units of Volt, from ADC

EGT      = round(EGT_data);                                //EGT now has exhaust gas temperature in degC integer.

CHT      = round(CHT_data);                                //CHT now has cylinder head temperature in degC integer.

//ECT is done in getSensor, cos its best averaging several samples to reduce noise.

//TDP                                                      MXP4425 pressure sensor on analog input
TDP      = (TDP_data * (s_5V/1023));                       //Volts, proportional to pressure
TDP      = ((TDP/s_5V)-0.04)/0.00369/100;                  //P = ((Vout/Vss)-0.04)/0.00369 kPa /100 Bar
TDP      = round((TDP*10)/10.0);                           //eliminate shed loads of (un-)precision.
                     
EOP      = map(EOP_data, 0, 1023, 0, 4);                   //Bar, universal pressure sensor on analog input, 0 - 4Bar = 0 - 1023ADC

EOT_data = DS_EOT.getTempC(DS_EOT_address);                //reads last assimilated temperature from the DS18B20 scratchpad to EOT_data
EOT      = round(EOT_data);                                //EOT now has engine oil temperature in degC integer.
}




//Scan Tool reader (Android Torque) must send us a message asking for the data associated with
//a PID, we read the message, and associate the request with a PID for which we have data.
//Mode 1 is for any actual current data (and it is dynamic)
//Mode 2 is for recorded Trouble Codes, for devices that have *previously* provoked memorisation of data due to out of range PID value
//Mode 3 is for clear Device Trouble Codes.. and would mean there is no Mode 2 data recorded

//so we only need Mode 2 comms.
//the Reader sends us a message containing the PID and we respond with the PID and its value
//the Reader sends us an 8 byte message:
//byte 1     2      3    4     5     6    7    8   
//   mode  format  PID  byte4 . . . .        byte8
//the PID is one byte
//the data for a PID, its value, is between 1 and 4 bytes, dependent on value range.
//
//Mode 1 PID 00 is special and if sent by the Reader, requires the ECU (us) to return a meesage to it
//listing all the PID's supported. So later the Reader will need send to only us a PID for which we
//have data; other ECU's may be on the bus and will not respond to a message sent to another ECU.
//They also won't respond to an enquiry for which they are not responsible.
//the Reader sends messages (optionally) with source and target addresses, so a Mode 1 PID 00 message will
//be addressed appropriately.

including science, physics, mechanics, engineering ...

of all things.

Tech and technology

Gregory French

Carretera de Palmitos Park, 20

35109 El Tablero

Las Palmas

SPAIN

 

[email protected]

the tech of all things

www.000webhost.com