Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 7 of 7

Thread: Bad serial RX data when writing to SD card

  1. #1
    Junior Member
    Join Date
    Jul 2018
    Posts
    8

    Bad serial RX data when writing to SD card

    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;
    
    }

  2. #2
    Senior Member+ defragster's Avatar
    Join Date
    Feb 2015
    Posts
    6,969
    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.

  3. #3
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,502
    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?

  4. #4
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,502
    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.

  5. #5
    Junior Member
    Join Date
    Jul 2018
    Posts
    8
    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.

  6. #6
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    18,502
    Quote Originally Posted by ol boy View Post
    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 by PaulStoffregen; 07-09-2018 at 10:43 PM.

  7. #7
    Junior Member
    Join Date
    Jul 2018
    Posts
    8
    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.

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •