Bad serial RX data when writing to SD card

Status
Not open for further replies.

ol boy

Member
I have a program that is polling data from another micro at 20Hz at 115200 baud rate. Each packet of data is 112 bytes long. I'm receiving the data into an array and then parse out the high and low byte and turn the data back into human values. From there I do a for loop and write all the data I just placed in the array to the SD card. I'm also creating CAN messages to transmit along with receiving messages. Everything work like it should as long as the SD card is not installed into it's slot. The CAN messages are producing the desired outputs into a GUI. Once the project is powered down and the SD card placed in the holder and powered back up, I get a couple seconds of good CAN data on the GUI then it all goes to crap. I can pull the SD card and view the .csv file and see where all the serial referenced values get shifted to the right or to the left a few spots.

I expanded the size of the RX buffer in the serial3.h file to 128 bytes. I also change the priority from 64 to 2.

I'm on a Teensy3.2, SDfat, and FlexCAN, Serial3.

What sucks the most is I had this all working really well months back and now I'm having to fight it so bad.

Code:
#include <FlexCAN.h>
#include <kinetis_flexcan.h>

#include <SPI.h>
#include "SdFat.h"
#include <Servo.h>
#include <FreqMeasureMulti.h>  // Used to measure input pulses from the ECU for tach signal RPM.

#include <EEPROM.h>

elapsedMillis duration;
Servo ThrottleServo;
IntervalTimer Halfsec;
IntervalTimer Tenthsec;
IntervalTimer Tenhz;
FreqMeasureMulti RPMin;
FreqMeasureMulti CH3in;

///////////////pin assignment////////////////////////////////////////////////
//0- RX1 to RS232 IC
//1- TX1 to RS232 IC
//2- unused
//3- CAN tx
//4- CAN rx
//5- Ch3 input
int EnableIN = 6; // from 14pin input harness
//7- RX3 to uSquirt Mod
//8- TX3 to uSquirt Mod
//9- Servo Out
//10- CS to uSD Card-2
//11- DOUT to uSD Card-3
//12- DIN to uSD Card-7
//13- SCK/LED to uSD Card-5
int EnableOUT = 14;// to HT0440 for 12/5v power enable
int FPsignal = 15; // from uSquirt FP output
int BatVolt = A2; //pin 16
int FPaIN = A3;  //pin 17 from JST14-2
int PM5 = 18; // I2C SDA
int PM4 = 19; // I2C SCL
//20- Measure RPM from uSquirt
int IgnPwr = A7; // 21
int Fault = 22; // fault light 
int FPOUT = 23; // PWM or digital on for fuelpump relay
//ThrottleA = DAC; // analog TPS signal, representive of PWM input.


float SdValue[32];  // holds data to write to SD card.
uint8_t serbuff[113]; // holds serial data from MS2
uint8_t setbuff[2];  // settings buffer for changing EEPROM settings.
uint16_t value[5]; //place to hold HOBS values, time and file number
uint8_t serialBlast[32]; // place to hold for serial blast to AP.  
String hobs = "";  //  a way to receive ascii characters from the text doc.
 

//int baudr = 1000000;  //1Mb rate
FlexCAN CANbus(1000000);
static CAN_message_t txmsg,rxmsg;

#define ECUOUT 0x8
#define ECUIN 0x9
#define CANFAST 0x00
#define CANSLOW0 0x01
#define CANSLOW1 0x02
#define CANSLOW2 0x03
#define THROTTLECAL 0x05
#define THROTTLECMD 0x06
#define HARDCONFIG 0x0A
#define TOTALENGINETIME 0x13
#define POWERCYCLES 0x10
#define SWVERSION 0x0B
#define USERSETTING 0x14
#define USERDATA 0x60
 
uint8_t serial = 0;
uint8_t k = 0;
uint8_t SdGood, seconds, CAN_seconds, i, fault, TPS8, ServoOUT_H, ServoOUT_L, Input_type, IGN8, flow;
uint8_t counter1, counter2, counter3, count, CANinput, CHT8c, BATV8, baroH, baroL, SDfull;
uint32_t Eminutes, engineRev, EngineTime, steps; // engine minutes
int inChar, inchar, number;
int b = 1; // sets fuel pump on during power up.
int location = 0;
uint16_t fuelUsed, RPM, ServoIN, ServoOUT, powercycles, firstFile, PWraw;
uint16_t ECUID, CAN_Throttle, TPSadc;
uint8_t ENABLE, CANBUS, TPSINPUT, FUELPUMP, MAP8;
uint32_t CANinputMessage;
uint8_t Status, ChargTemp, InjDutyH, InjDutyL;  // ECU condition  enable and serial mode.
int8_t MAT8c;
uint8_t EngineTime0, EngineTime1, EngineTime2, syscmd,g_hrH,g_hrL;
uint16_t FuelUsedDiv,ThrottleClosed, ThrottleOpen;
float slope, yint, g_hr;
uint8_t gramsL, gramsML, gramsMH, gramsH;
uint16_t deadtime, grams;


//File object
//SD chip select pin.
const uint8_t chipSelect = SS;
SdFat sd;

SdFile file;   // for test.txt file
SdFile logfile;  // for .csv file

//char newfile[13] = "D0000.csv";  // base name for .csv file.  updat positions 1,2,3,4 from .txt file
char newfile[13];  // base name for .csv file.  updat positions 1,2,3,4 from .txt file
char firstfile[13]; //  array for the first file to be deleted.


/////////////////////////////////////////////Set Up///////////////////////////////////////////////////
void setup() {
 pinMode(EnableIN, INPUT);   // read input state for Enable
 pinMode(EnableOUT, OUTPUT);  // drive the HT0440 to power Inj and Ign.
 pinMode(FPsignal, INPUT);  // read state of FP drive pin from uSquirt Module
 pinMode(BatVolt, INPUT);   // measure battery voltage level
 pinMode(FPaIN, INPUT);     // measure fuel pressure
 pinMode(PM5, INPUT);       // read state of ECU pin
 pinMode(PM4, INPUT);       // read state of ECU pin
 pinMode(IgnPwr, INPUT);    // measure 5v reg voltage level
 pinMode(Fault, OUTPUT);    // drive fualt lamp
 pinMode(FPOUT, OUTPUT);    // drive fuel pump relay
 
digitalWrite(FPOUT,b);  // on power up turn on Fuel Pump relay.
SDfull = EEPROM.read(12); // check for prior SD car volume with less than 750MiB

IntSdCard(); // start SD card. 

if(SdGood){  //if sd card good then open some files 
OpenHobs();   // open test.txt and get engine run time and current file name for logging
NewFile();    // create new .csv file for logging
writeHeader();  // write header info
}

Read_Eeprom();// Grab setting values from EEPROM.
Do_Math();

Halfsec.begin(HalfSec, 500000); 

RPMin.begin(20,FREQMEASUREMULTI_RAISING);// capture RPM input signal
CH3in.begin(5, FREQMEASUREMULTI_MARK_ONLY);// capture incoming throttle command.

Tenthsec.begin(TenthSec, 100000);// Serial3 output to AP.
delay(50);
Tenhz.begin(TwentyHZ, 50000); // 20 hz CAN fast out and ECU data request

Serial1.begin(115200);  // to RS232 then to JST14 connector for external tuning
Serial.begin(115200);   // for COM cummunication to laptop for setting EEPROM values
Serial3.begin(115200);  // UART lines to uSquirt module
CANbus.begin();
}

////////////////////////////////////////Main Loop/////////////////////////////////////////////////////
void loop() {
 checkSerial();
 readRPM();
 Channel3();
 GetCAN();

////////////Read FP signal from uSquirtModule/////////
if (!FUELPUMP){
int c = digitalRead(FPsignal);
digitalWrite(FPOUT,c);
}else{
  //Insert PWM code or subroutines here//////////
  
}
 uint16_t w = analogRead(BatVolt); // 47k to 6.8k  zero to 26.1 volts  
 float batteryVolt = w * 0.0255;  // convert ADC to volts.  ie.  512 adc X 0.0255 is 13.05V
 SdValue[29] = batteryVolt;
 uint16_t z = analogRead(FPaIN);  //1k to 1k v-divid 6.6v max.
 SdValue[22] = z;
 uint16_t d = analogRead(IgnPwr);  //1k to 1k v-divid.  6.6V max input.
 SdValue[25] = d;
 if (d <= 690){
  fault = fault | 2; ;
 }
  
}

////////////////////////////////Read EEPROM settings/////////////////////////////////////////////////
void Read_Eeprom(){// read eeprom data for settings options
  ECUID = (EEPROM.read(0)<<8) | EEPROM.read(1); // get stored ECU ID/address
  ENABLE = EEPROM.read(2);  // 0= TPSenable, 1= Digital Enable, 2, TPS_CAN, 3= CAN/Digital Enable
  CANBUS = EEPROM.read(3);  // 0= off, 1=on(fast,slow0,slow1,slow2)
  TPSINPUT = EEPROM.read(4); // 0=PWM(50hz), 1=CAN, 2=CAN/PWM
  FUELPUMP = EEPROM.read(5); // 0= on/off, 1= PWM w/CL pressure feedback
  FuelUsedDiv = (EEPROM.read(6)<<8) | EEPROM.read(7);  // need to sort this out. Call out injector, 50, 150, 250, 450.
  ThrottleClosed = (EEPROM.read(8)<<8) | EEPROM.read(9); // Throttle Closed pulse 800 to 1100.  (3,132)=900
  ThrottleOpen = (EEPROM.read(10)<<8) | EEPROM.read(11); // Throttle Open Pulse 1800 to 2200.   (8,52)= 2100
  //CANinputMessage = 0x906 << 16 | ECUID; // From AP for throttle command Group 9, FrameID 6, ECUID.
  ThrottleServo.attach(9, ThrottleClosed, ThrottleOpen);  // set min to 900us and max to 2100us  SERVO out
 
}


///////////////////////////////Do Math for pulsein to analog out./////////////////////////////////////
void Do_Math(){
uint16_t delta = ThrottleOpen - ThrottleClosed;
slope = 1000/(float)delta;
yint = ThrottleClosed * slope;
// y = mx+b
//throttle% = inputpulse(slope) + yint.
//outputpulse = inputpulse.

}

/////////////////////////////////Record input pulse from AP or RX//////////////////////////////////////
void Channel3(){
 if (CH3in.available()){
 unsigned long a = CH3in.read(); 
 ServoIN = a/24 - 10;  // commanded throttle
 counter2 = 0;
 }
  
  if(!TPSINPUT){  // PWM in only
  if (counter2 >= 5){
    fault = fault | 16;
    ServoIN = 0;
    Input_type = 0x00;
  }   
  
  SdValue[21] = ServoIN;
  ServoOUT = ServoIN;
  Input_type = 0x20;
  
 }else if(TPSINPUT == 1){// CAN only
   SdValue[21] = CAN_Throttle; // zero to 255.  0.39% per step
   float candelta = ThrottleOpen-ThrottleClosed;
   ServoOUT = ((candelta/256) * CAN_Throttle)+ ThrottleClosed;
   Input_type = 0x80;
   
  }else if(TPSINPUT == 2){// CAN with backup PWM
    
  }

  
    if(ServoOUT >= ThrottleOpen){
      ServoOUT = ThrottleOpen;
    }else if(ServoOUT <= ThrottleClosed){
      ServoOUT = ThrottleClosed;
    }else {
     //SerovOUT = ServoIN;
    }
   
  uint16_t throttle = ((float)ServoOUT * slope) - yint; //  y=mx-b
  TPSadc = throttle * 4;  // convert xx.x to 0to4000.
  analogWriteResolution(12);  // zero to 4095
  analogWrite(A14, TPSadc); // send new DAC value
  ThrottleServo.writeMicroseconds(ServoOUT);  //Update the servo position
  ServoOUT_L = ServoOUT & 0xFF;// just grab and keep the low byte
  ServoOUT_H = (ServoOUT >> 8 | Input_type);  // just keep the high byte and or in "Input_type"
  
  

if(!ENABLE){
if(ServoIN >= (ThrottleClosed + 50)){
    digitalWrite(EnableOUT, HIGH);
    Status = 0x01;
  }else{
    digitalWrite(EnableOUT, LOW);
    Status = 0x00;
  }
}else if (ENABLE == 1){// Digital in ENABLE here/////////////
  int h = digitalRead(EnableIN);
  if(h){
     digitalWrite(EnableOUT,HIGH);
     Status = 0x01;
  }else{
    digitalWrite(EnableOUT, LOW);
    Status = 0x00;
  }
 }else if(ENABLE == 2){// Digital and/or CAN input here////////
   if(ServoOUT >= (ThrottleClosed + 50)){
    digitalWrite(EnableOUT, HIGH);
    Status = 0x01;
  }else{
    digitalWrite(EnableOUT, LOW);
    Status = 0x00;
  }
   }
 }



////////////////////Get RPM if present/////////////////////////////////////
void readRPM (){//Set up for 24Mhz 
 
  int batcor;
  if (RPMin.available()){
 unsigned long a = RPMin.read();// have to read the captured measement.
 
  counter1 = 0;
 engineRev++;

//Read PW..  find fuel amount for single pulse at that PW.. Maybe assign a value 0 to 1023 for the pulse. 9.77us per step.
//add the value to itself, then divide by a diviser to get true fuel used.   
//

if(SdValue[8] >= 8.0){
    batcor = (13.2 - SdValue[8]) * 100; // solve deatime of injector.
    deadtime = 700 + batcor;
    steps += (PWraw - deadtime); //   keep adding PWraw minus deatime value to steps
   uint16_t math2 = PWraw - deadtime;
   
}

 float flowrate = (PWraw-deadtime) / (float)FuelUsedDiv;  // instant flow of fuel in g/min...
 g_hr = flowrate * 30;  // go from g/min to g/hr 
 SdValue[28] = g_hr; 
 g_hrH = (uint16_t)g_hr >> 8;  // grab high byte
 g_hrL = (uint16_t)g_hr & 0xFF; // grab low byte

 
 if(steps >= (FuelUsedDiv * 6000)){ 
 grams++; // add one more gram used once steps adds up to more than fuelused(us) * 6000. 1 mintes worth of PW.
 steps = 0;
 SdValue[19] = grams;
 
 }
  
  gramsL = (uint16_t)grams & 0xFF;  //grab the low byte
  gramsML = (uint16_t)grams>>8 ; // grab the 2nd byte  good to 144lbs of fuel
  gramsMH =  0;
  gramsH =  0;
       
    if (counter1 >= 10){
    RPM = 0;
    }
  
  if(SdValue[0] == 0){
    SdValue[28] = 0;
    SdValue[23] = 0;
    //SdValue[] = 0; 
  }
  }
  }



//////////////////////////get serial data/////////////////////////////////////
void checkSerial(){
  
 if (counter3 >= 100){ // check if JST14 COM port has been inactive for 5 sec
  serial = 0;           // resume data request from MS2
   }

///////////////////USB serial input for changing settings////////////////////////////
 if(Serial.available() > 0){ // setting input from teensy COM port.
  inChar = Serial.read();   // only used to write new values into eeprom
  setbuff[i] = inChar;
  i++;    
 }

if(i >= 2){
     if(setbuff[1] == 255){
      for (int y=0; y<20; y++){
        int val = EEPROM.read(y);
        Serial.print(y);
        Serial.print("\t");
        Serial.println(val);   
                              }          
       }else{   
        EEPROM.write(setbuff[0], setbuff[1]);  //address then value to write to that address.
       } 
       setbuff[0] = 0;
       setbuff[1] = 0;
       i = 0;
}
///////////////////End of USB serial checking/////////////////////////////////////////

//////////////////JST14 pin Serial input check/////////////////////////////////////// 
 if(Serial1.available() > 0){ // check for data being used from JST14 COM port
    serial = 1;             // stops teensy from requesting data from MS2
    counter3 = 0;           // keep track of when data is nolonger being sent from and to JST14 COM port      
 }

///////////////////Receive data from MS2//////////////////////////////////////////  
  if(!serial){              // receive data from MS2 
// if (Serial3.available() >0){
//    inChar = Serial3.read();  // read data into array serbuff
//    serbuff[k] = inChar;
//    k++;                      // keep track of number fo bytes received.
//  }
while(Serial3.available() && k <= 112){
  inChar = Serial3.read();  // read data into array serbuff
  serbuff[k] = inChar;
  k++;                      // keep track of number fo bytes received.
}
    
  }else{    // used for passing data in and out of JST14 COM port.
    if (Serial3.available() >0 ){ // read from MS2
    inChar = Serial3.read();
    Serial1.write(inChar);  // to JST14
     }
    if (Serial1.available() > 0){  // read from JST14
      inChar = Serial1.read();
      Serial3.write(inChar);  // to MS2
    }
  }
 

 if(!serial){
  if (k >= 112){
    Serial3.clear();  // serial3 buffer, clear buffer
    pickdata();
    storeData();
    k = 0;
  }else{
    //Serial.println("K not counting");
  }
  } else{
    //Serial.println("Serial = 1"); 
  }
} 



///////////////////////////pick out data from serial buffer/////////////////////////////////////
void pickdata(){
    SdValue[0] = (serbuff[6]<<8) | (serbuff[7]); //rpm
    serialBlast[0] = serbuff[6];  //rpm high
    serialBlast[1] = serbuff[7];  //rpm low
    short tps = ((serbuff[24]<<8) | serbuff[25]); //tps
    serialBlast[2] = serbuff[24]; //tps high
    serialBlast[3] = serbuff[25];  //tps low
    TPS8 = tps / 3.921; // converts float to 8bit 0-255 value for CAN bus.
    SdValue[1] = (float) tps / 10.0;    
    SdValue[2] = (((serbuff[18]<<8) | serbuff[19]) / 10.0); //map
    serialBlast[8] = serbuff[18]; // map high
    serialBlast[9] = serbuff[19];  // map low
    MAP8 = SdValue[2]/SdValue[3] * 100;  // MAP/BARO x 100. give ratio zer to 100%
    SdValue[3] = (((serbuff[16]<<8) | serbuff[17]) / 10.0); // baro
    serialBlast[10] = serbuff[16]; //baro high
    serialBlast[11] = serbuff[17]; //baro low
    uint16_t baro = ((serbuff[16]<<8) | serbuff[17])* 50;
    baroH = baro >>8;
    baroL = baro & 0xFF;
    short cht =  ((serbuff[22]<<8) | serbuff[23]);  // CHT
    //short CHT8 = cht / 10;
    CHT8c = (((cht - 320) * 5/90)+10);  // converts F to C and in 8-bit scale -10C to 245C.
    SdValue[4] = (float) cht / 10.0;
    short mat = ((serbuff[20]<<8) | serbuff[21]); // mat
    short MAT = mat / 10;
    MAT8c = (MAT - 32) *5/9;  //int8-t value for -128 to 127C for IAT
    ChargTemp = ((MAT8c + 128)*2/3);// weird charge temp math here...
    SdValue[5] = (float) mat / 10.0;
    SdValue[6] = (((serbuff[2]<<8) | serbuff[3]) / 1000.0); // PW
    PWraw = ((serbuff[2]<<8) | serbuff[3]); // PW raw uint16_t value
    serialBlast[15] = serbuff[2];  // PW high
    serialBlast[16] = serbuff[3];  // PW low
    SdValue[7] = (((serbuff[8]<<8) | serbuff[9]) / 10.0); // ign advance
    serialBlast[13] = serbuff[8]; // ign advance high
    serialBlast[14] = serbuff[9]; // ign advance low
    IGN8 = ((serbuff[8]<<8) | serbuff[9])/5;// work this, output on CAN wrong.
    SdValue[8] = (((serbuff[26]<<8) | serbuff[27]) / 10.0);//batv
    BATV8 = serbuff[27];
    serialBlast[17] = serbuff[27]; // bat volt  255 is 25.5 volts
    SdValue[9] = (((serbuff[38]<<8) | serbuff[39]) / 10.0); // air correction 
    SdValue[10] = ((serbuff[40]<<8) | serbuff[41]) ; // warmup correction
    SdValue[11] = (((serbuff[46]<<8) | serbuff[47]) / 10.0); // baro correction
    short tpsdot = ((serbuff[58]<<8) | serbuff[59]); // TPSdot
    SdValue[12] = (float) tpsdot / 10.0;
    short mapdot = ((serbuff[60]<<8) | serbuff[61]); //MAPdot
    SdValue[13] = (float) mapdot / 10.0;
    SdValue[14] = (((serbuff[50]<<8) | serbuff[51]) / 10.0); // VE1
    SdValue[15] = (((serbuff[28]<<8) | serbuff[29]) / 10.0); //AFR    
    serialBlast[12] = serbuff[29]; // AFR in 8 bit form.  10 to 20 AFR  100 to 200..
    SdValue[16] = (((serbuff[34]<<8) | serbuff[35]) / 10.0); //EGO correction
    SdValue[17] = (((serbuff[74]<<8) | serbuff[75]) / 10.0); // egoV
    SdValue[18] = ((serbuff[48]<<8) | serbuff[49]); // gammeEnrich
    //SdValue[19] = fuelUsed; // grams total
    SdValue[20] = Eminutes; //
    serialBlast[25] = Eminutes >> 8;  // Engine run time High byte
    serialBlast[26] = Eminutes | 0xFF;   // Engine run time Low byte
//    EngineTime2 = Eminutes >> 16;
//    EngineTime1 = (Eminutes >> 8) & 0xFF;
//    EngineTime0 = Eminutes & 0xFF;
    //SdValue[21] = ServoIN;  // uS 900 to 2100
    //SdValue[22] = Fuelpressure; // 0 to 100...  maybe..  //SdValue[22] = c; // 0 to 100...  maybe.. 0 - 1023
    //SdValue[23] = Flowrate;  //lbs_hr..
   // int c = analogRead(FPaIN);
    SdValue[24] = engineRev;
    //SdValue[25] = ignition voltage;
    SdValue[26] = Status;
    //long InjDuty =  (((serbuff[2]<<8) | serbuff[3]) * SdValue[0]) / 60000; //PW x Rpm / 600.
    SdValue[27] = SdValue[6] * SdValue[0] / 600;  // solves injector duty cycle.//
    uint16_t InjDuty = SdValue[27] * 10;
    InjDutyH = InjDuty >> 8;
    InjDutyL = InjDuty & 0xFF;
    
    //SdValue[28] = grams/hr;
    //SdValue[29] = batteryVolts;
    //SdValue[30] = freeSpace;
  }

///////////////////////////////////Receive CAN///////////////////////////////////////////////
void GetCAN(){
  while ( CANbus.read(rxmsg) ) {
//when message available
if(rxmsg.id == (ECUIN<<24 | THROTTLECMD<<16 | ECUID)){
 
  CAN_Throttle = rxmsg.buf[0]; // need to sort this to servo limits...
  CANinput = 1;
}

 else if(rxmsg.id == (ECUIN<<24 | HARDCONFIG<<16 | ECUID)) {   //Request ECU ID and fuel divisor value
 if (rxmsg.len == 0){
    txmsg.len = 8;
    txmsg.id = (ECUOUT<<24 | HARDCONFIG<<16 | ECUID);
    txmsg.buf[0] = EEPROM.read(0); // ECU Id high
    txmsg.buf[1] = EEPROM.read(1); // ECU low
    txmsg.buf[2] = EEPROM.read(6);
    txmsg.buf[3] = EEPROM.read(7); //Fuel used H  work on this later.  Maybe have the 4 injector flow rates sorted 1-4 in EEPROM
    txmsg.buf[4] = 0; //
    txmsg.buf[5] = 0; // 
    txmsg.buf[6] = 0; // 
    txmsg.buf[7] = 0; // 
    CANbus.write(txmsg);    //SEND IT!
 }
 }
 else if (rxmsg.id == (ECUIN<<24 | THROTTLECAL<<16 | ECUID)){   // throttle calibration stuff.
      if (rxmsg.len == 0){
        txmsg.len = 8;
        txmsg.id = (ECUOUT<<24 | THROTTLECAL<<16 | ECUID);
        txmsg.buf[0] = EEPROM.read(8);
        txmsg.buf[1] = EEPROM.read(9);
        txmsg.buf[2] = EEPROM.read(10);
        txmsg.buf[3] = EEPROM.read(11);
        txmsg.buf[4] = 0; //
        txmsg.buf[5] = 0; // 
         
        CANbus.write(txmsg);    //SEND IT!
        }
          else if(rxmsg.len >= 4){
           uint8_t closedH = rxmsg.buf[0];
           uint8_t closedL = rxmsg.buf[1];
           uint8_t openH = rxmsg.buf[2];
           uint8_t openL = rxmsg.buf[3];
           EEPROM.write(8,closedH);
           EEPROM.write(9,closedL);
           EEPROM.write(10,openH);
           EEPROM.write(11,openL);

           Read_Eeprom();
           Do_Math();
          }
          }
else if (rxmsg.id == (ECUIN<<24 | TOTALENGINETIME<<16 | ECUID)){ // totlat engine time in seconds
    if(rxmsg.len == 0){      
      uint32_t engineseconds = Eminutes * 60;
      txmsg.len = 3;
      txmsg.id = (ECUOUT<<24 | TOTALENGINETIME<<16 | ECUID);// 90
      txmsg.buf[0] = engineseconds >> 16;     //   seconds for the high byte
      txmsg.buf[1] = (engineseconds >> 8 ) & 0x00FF; // seconds for mid byte
      txmsg.buf[2] = engineseconds & 0x0000FF; //   seconds for low byte.
      CANbus.write(txmsg);  // SEND IT!
    }  
      }else if(rxmsg.id == (ECUIN<<24 | POWERCYCLES<<16 | ECUID)){  // power cycle count, pull from SD file number
        if(rxmsg.len == 0){  
           txmsg.len = 8;
           txmsg.id = (ECUOUT<<24 | POWERCYCLES<<16 | ECUID);
           txmsg.buf[0] = powercycles >> 8; // grab high byte
           txmsg.buf[1] = powercycles & 0xFF; // grab low byte
           txmsg.buf[2] = 0;
           txmsg.buf[3] = 0;
           txmsg.buf[4] = 0;
           txmsg.buf[5] = 0;
           txmsg.buf[6] = 0;
           txmsg.buf[7] = 0;
           CANbus.write(txmsg);  // SEND IT!
        }
      }else if(rxmsg.id == (ECUIN<<24 | SWVERSION<<16 | ECUID)){ // Firmware version request and replay
        if(rxmsg.len == 0){  
           txmsg.len = 8;
           txmsg.id = (ECUOUT<<24 | SWVERSION<<16 | ECUID);
           txmsg.buf[0] = 1; // major firmware version
           txmsg.buf[1] = 12;   // minor firmware version
           txmsg.buf[2] = 4;  // month
           txmsg.buf[3] = 19;  // day
           txmsg.buf[4] = 0x07;  // year high byte
           txmsg.buf[5] = 0xE2;  // year low byte
           txmsg.buf[6] = 0xAA;  //check sum high
           txmsg.buf[7] = 0x55;  // check sum low
           CANbus.write(txmsg);  // SEND IT!
        }
      }else if(rxmsg.id == (ECUIN<<24 | USERSETTING<<16 | ECUID)){
          uint8_t highB;
          uint8_t lowB;
          syscmd = rxmsg.buf[0];
          if(syscmd == 0x11){   // fuel divisor setting
            highB = rxmsg.buf[1];
            lowB = rxmsg.buf[2];
            EEPROM.write(6,highB) ;  //Fuel divisor high
            EEPROM.write(7,lowB) ;  //Fuel divisor low
          }else if (syscmd == 0x10){
            grams = 0;
          }else if(syscmd == 0x50){  // ECU address.
            highB = rxmsg.buf[1];
            lowB = rxmsg.buf[2];
            EEPROM.write(0,highB) ;  //ECU ID high
            EEPROM.write(1,lowB) ;   //ECU ID low
            Read_Eeprom();
          }else if (syscmd == 0x60){
            uint8_t data1 = rxmsg.buf[1]; // address
            uint8_t data2 = rxmsg.buf[2]; // data to place into address
            EEPROM.write(data1, data2);
             }
             Read_Eeprom();
          }     
    }
 }


///////////////////////////////////////CAN FAST Messages///////////////////////////////////////
  void FastCan(){
    txmsg.len = 8;
    txmsg.id = (ECUOUT<<24 | CANFAST<<16 | ECUID);
    //txmsg.id = 0x08xxxx;  //Fast CAN
    txmsg.buf[0] = TPS8;
    txmsg.buf[1] = serbuff[6];  //rpm H
    txmsg.buf[2] = serbuff[7];  //rpm L
    txmsg.buf[3] = gramsH; //Fuel used H  work on this later.  Maybe have the 4 injector flow rates sorted 1-4 in EEPROM
    txmsg.buf[4] = gramsMH; //Fuel used  MH
    txmsg.buf[5] = gramsML; // fuel used ML
    txmsg.buf[6] = gramsL; // fuel used L
    txmsg.buf[7] = Status; // status  0x01 when enabled, 
    CANbus.write(txmsg);    //SEND IT!
  }

///////////////////////////////////////CAN SLOW_0 Messages///////////////////////////////////////
  void Slow0Can(){
    txmsg.ext = 1;
    txmsg.len = 8;
    txmsg.id = (ECUOUT<<24 | CANSLOW0<<16 | ECUID);
    //txmsg.id = 0x08xxxx;  //Slow0 CAN
    txmsg.buf[0] =  0;  //commanded RPM  50 rpm count
    txmsg.buf[1] = ServoOUT_H;  //8-bit TPS 0-100%
    txmsg.buf[2] = ServoOUT_L; //  12-bit count of uS pulse to servo
    txmsg.buf[3] = CHT8c; //  CHT 8-bit -10 to 245 *C
    txmsg.buf[4] = 0; // none
    txmsg.buf[5] = baroH; // baro H
    txmsg.buf[6] = baroL; // baro L  unit of 2 pascals
    txmsg.buf[7] = MAP8; // MAP ratio 0 - 100%  or MAP kpa

    CANbus.write(txmsg);    //SEND IT!
  }
///////////////////////////////////////CAN SLOW_1 Messages///////////////////////////////////////
  void Slow1Can(){
    txmsg.ext = 1;
    txmsg.len = 8;
    txmsg.id = (ECUOUT<<24 | CANSLOW1<<16 | ECUID);
    //txmsg.id = 0x08xxxx;  //Slow1 CAN
    uint16_t pressure = SdValue[22] * 6;
    txmsg.buf[0] = MAT8c;  // IAT *C  -128 to 127
    txmsg.buf[1] = pressure >> 8;  //Fuel Pressure H
    txmsg.buf[2] = pressure & 0xFF; //Fuel Pressure L
    txmsg.buf[3] = EngineTime2; //Engine Time_2
    txmsg.buf[4] = EngineTime1; //Engine Time_1
    txmsg.buf[5] = EngineTime0; //Engine Time_0
    txmsg.buf[6] = BATV8; //Batv  8-bit xx.x
    txmsg.buf[7] = 0; //Status for RPM control, zero = off.

    CANbus.write(txmsg);    //SEND IT!
  }
///////////////////////////////////////CAN SLOW_2 Messages///////////////////////////////////////
  void Slow2Can(){
    txmsg.ext = 1;
    txmsg.len = 8;
    txmsg.id = (ECUOUT<<24 | CANSLOW2<<16 | ECUID);
    //txmsg.id = 0x08xxxx;  //Slow2 CAN
    txmsg.buf[0] = 25;  //CPU load
    txmsg.buf[1] = ChargTemp;  //Charge Temp
    txmsg.buf[2] = InjDutyH; //Injector duty H
    txmsg.buf[3] = InjDutyL; //Injector duty L
    txmsg.buf[4] = IGN8; // Ign Angle1
    txmsg.buf[5] = 0; // Ign Angle2
    txmsg.buf[6] = g_hrH; //Flow Rate H g/hr>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
    txmsg.buf[7] = g_hrL; //Flow Rate L g/hr>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

    CANbus.write(txmsg);    //SEND IT!
  }

  
/////////////////////////////////////open engine run time//////////////////////////////////
void OpenHobs(){  
    file.open("test.txt", O_RDWR);  // open test.txt for reading and writing. O_READ
    while (file.available()){
    inchar = file.read();    
     if (inchar != '\n'){
    hobs += (char)inchar;
    }else{      
     value[location] = hobs.toInt();  // convert read values into int.  
     location++;    
     hobs = "";  //clear hobs array. 
      }    
    }     
      
    Eminutes = value[0];   // pull int value from array Engine time
    number = value[1];      // pull int value from array new file name
    firstFile = value[2];
    powercycles = number;

    if(SDfull == 1){      // if less than 750MB left on the card start erase the oldest files.
      firstfile[0] = 'D';  // place the 'D' to the beginning. 
      itoa(firstFile, firstfile+1, 10);  

       if(firstFile / 10000 >= 1){ // test the value of number and load newfile array in the proper spots.
        firstfile[6] = '.';
        firstfile[7] = 'c';
        firstfile[8] = 's';
        firstfile[9] = 'v';  
        }else if(firstFile / 1000 >= 1){ // if greater than 1000 add .csv
        firstfile[5] = '.';
        firstfile[6] = 'c';
        firstfile[7] = 's';
        firstfile[8] = 'v';
        }else if(firstFile / 100 >=1){
        firstfile[4] = '.';
        firstfile[5] = 'c';
        firstfile[6] = 's';
        firstfile[7] = 'v';
        }else if(firstFile / 10 >=1){
        firstfile[3] = '.';
        firstfile[4] = 'c';
        firstfile[5] = 's';
        firstfile[6] = 'v';
        }else {
        firstfile[2] = '.';
        firstfile[3] = 'c';
        firstfile[4] = 's';
        firstfile[5] = 'v';
        }        
        firstFile++;
    }

       ////////////convert int back to array ascii values. not working quite right.
       //File name will grow over time and has a fluid length.  
        newfile[0] = 'D';  // place the 'D' to the beginning.  
       itoa(number, newfile+1, 10);  //converts int to array base 10 after the D.. 

        if(number / 10000 >= 1){ // test the value of number and load newfile array in the proper spots.
        newfile[6] = '.';
        newfile[7] = 'c';
        newfile[8] = 's';
        newfile[9] = 'v';  
        }else if(number / 1000 >= 1){ // if greater than 1000 add .csv
        newfile[5] = '.';
        newfile[6] = 'c';
        newfile[7] = 's';
        newfile[8] = 'v';
        }else if(number / 100 >=1){
        newfile[4] = '.';
        newfile[5] = 'c';
        newfile[6] = 's';
        newfile[7] = 'v';
        }else if(number / 10 >=1){
        newfile[3] = '.';
        newfile[4] = 'c';
        newfile[5] = 's';
        newfile[6] = 'v';
        }else {
        newfile[2] = '.';
        newfile[3] = 'c';
        newfile[4] = 's';
        newfile[5] = 'v';
        }
        number++;  // add 1 to make new number name for next power cycle.
        file.rewind();  // start at begining of file
        file.println(Eminutes);  // write engine run time
        file.println(number);     // write new file name for next power cycle.
        file.println(firstFile); // write first file number here.
        file.close();           // close file

    if(SDfull == 1){
     sd.remove(firstfile);// remove
    }

    }

 
//////////////////////////////////////////SD card Int/////////////////////////////////////////////////////
void IntSdCard(){
if(!sd.begin(chipSelect, SPI_FULL_SPEED)){
  sd.initErrorHalt();
  SdGood= 0;
}else{
  SdGood = 1;
  uint32_t volFree = sd.vol()->freeClusterCount();
  SdValue[30] = 0.000512*volFree*sd.vol()->blocksPerCluster(); // find the remaining data left in the sd card. x.xx GB.

    if((SDfull == 0) && (SdValue[30] < 2850)){  // check if we have set full bit once we are below 750MB remaining
       EEPROM.write(12,1);  // write to location 12 a 1.
    }else if((SDfull == 1) && (SdValue[30] > 2900)){  // check if full bit is still set and there is more than 1GB free space.
      EEPROM.write(12,0); // write zer0 to location 12.
    }
    }  
}

/////////////////////////////////////// Write data header///////////////////////////////////////////////
void writeHeader() {  
  logfile.println("Engine, Log"); 
  logfile.println("TIME,RPM,TPS,MAP,BARO,CHT,IAT,PW,IgnAdv,BATV,AirCorr,WUE,BaroCor,TPSdot,MAPdot,VE1,AFR,EGOCor,egoV,gaErich,FuelUsed,EngineTime,ServoIN,FuelPressure,LbsHr,CrankRev,IgPwr,Status,InjDuty,g_hr,batV,SDvol"); 
  logfile.close();
}

//////////////////////////////////New File/////////////////////////////////////////////////
void NewFile(){
 if(!logfile.open(newfile, O_CREAT | O_WRITE | O_EXCL)){ 
}
 
}



//////////////////////////////////////Write to SD card///////////////////////////////////////
void storeData(){
float tenthSec = duration / 1000.00;
    if(!logfile.open(newfile, O_WRITE | O_AT_END)){
      logfile.print(tenthSec,3);
      logfile.print(",");
      for(int x = 0; x < 31; x++){
        logfile.print(SdValue[x]);
        logfile.print(",");
      }
      logfile.println();
      count++;
      }
      if (count >= 250){ // close reopen every 5 sec.
        logfile.close();
        count = 0;
      }
  
}

///////////////////////////////////////update minutes of run time//////////////////////////
void WriteNewHOBS(){
logfile.close();  //if log file open then close it before opening the HOBS.csv file
file.open("test.txt", O_WRITE);// Open HOBS.csv 
file.rewind();// move to beginning of the file to over write the last minute.
file.print(Eminutes);
file.close(); 

}

/////////////////////////20hz data request/////////////////////////////////////////////////////
void TwentyHZ(){
  if(!serial){         
  Serial3.write(97);// request/pole data from MS2, send a06.
  Serial3.write(0);
  Serial3.write(6);  
  i = 0;
  }else{
    counter3++;
    if(counter3 >= 110)
    counter3 = 105;
  }

  if(CANBUS){
    FastCan();
  }
}


/////////////////////////////////Serial Blast to AP//////////////////////////////
void BlastSerial(){
  int z;
for(z = 0; z < 27; z++){  // write 27 bytes to JST serial out.
  Serial1.write(serialBlast[z]);  
}
}

////////////////////////request next engine data sample//////////////////////////////////////
void TenthSec(){
  counter1++;
  counter2++;
  if(!serial){
    BlastSerial();
  }
  
  }

  
////////////////////////place in side 0.5 sec interval timer/////////////////////////////
void HalfSec (){

if(SdValue[0] >= 500){
seconds++;
CAN_seconds++;
}else{
  seconds = 0;
  CAN_seconds = 0;
}

if(CANBUS){ // if CAN messages are enabled send them
Slow0Can();
Slow1Can();
Slow2Can();
}

if (seconds >= 119){
  seconds = 0;
  Eminutes++; 
  WriteNewHOBS();
  //SdValue[20] = Eminutes;   
}
if(CAN_seconds >= 2){
  EngineTime++; //engine time is seconds for CANSlow1
  CAN_seconds = 0;
}

    EngineTime2 = EngineTime >> 16;
    EngineTime1 = (EngineTime >> 8) & 0xFF;
    EngineTime0 = EngineTime & 0xFF;

}
 
Serial3 doesn't have the 8 byte hardware FIFO of Serial1 and 2, just one byte requiring a interrupt to capture each character.

Moving to one of those - even if using the alternate pins for them might solve the issue.
 
Looks like you're writing to the SD card from within the HalfSec() interrupt. I didn't fully unravel your program's structure, but that's the one case I can see where an interrupt accesses the card. I also see lots of Serial writing, but those should go fast if the serial transmit buffers have enough space.

Ideally you should structure your program to avoid SD card access or other operations which could take a long time. Maybe store the data into an array and set a flag, so loop() can notice the flag and do the SD card writing without any interrupt running while the SD card takes time to do its work.

Or a simple but less ideal workaround might be to lower the interrupt priority (higher number) for the Halfsec timer. Maybe try Halfsec.priority(255); Do this right after Halfsec.begin(), not inside the interrupt itself. Does that help?
 
One simple way to diagnose interrupt times is with a digitalWriteFast(pin, HIGH) at the beginning of every interrupt and digitalWriteFast(pin, LOW) at the end. Then watch the pin with an oscilloscope or logic analyzer, or even just an ordinary DC voltmeter. Ideally the pin should go high for very short times. Obviously there's not much you can see with a voltmeter, but a voltage more than several millivolts is a strong sign some interrupt it taking too long. On a scope you should see the long pulses, and if you have a modern scope with pulse width or "zone" trigger you can set it to avoid triggering on the short ones.

If you do see long pulses or more than a few mV, just commenting out the digitalWriteFast(pin, HIGH) lines one by one can get you to a pretty good understanding of which interrupt is the culprit consuming too much time. Then you can lower its priority or restructure your program so the lengthy operation is done later from the main program.
 
Thanks Paul.

Writing to the SD card in the halfsec() is to update the total engine run time. The values read from the serial data are stored in SdValue[] array. In the storedata() function I write the array into the SD buff using a for loop, this happens at the end of parsing the serial stream.

What's killing me is this all worked a few months back when I was chasing a fuel used idea and we did a company wide migration of the computers data, so now all data is stored on the server and not in the laptops.
 
What's killing me is this all worked a few months back

Maybe you were using a different SD card? Write speed vary quite a lot between cards. Or maybe the card was in new condition and had not yet remapped sectors or done other internal media management which increases latency?

Edit: just to be clear, I'm not suggesting to solve your problems by replacing the SD card. Doing this stuff inside the interrupt is risky business. It's the sort of design that makes a project sensitive to the timing of the SD card. A good long-term fix should avoid writing to the SD card from that interrupt.
 
Last edited:
I thought that too so I reformated the card. I'm still seeing lost serial data even without the card installed. I'm going to find a different computer to do this from and see what happens, stay tuned.
 
Status
Not open for further replies.
Back
Top