Search code examples
cfile-ioraspberry-pisegmentation-faulti2c

C - segfault at 1021 iterations and unable to open i2c at 1020 iterations


Hi I'm having an issue continuously recording data to a .csv file using the following script

    int ddm(void)
{   
//  96 Temp MSB,    97 Temp LSB,    98 Vcc MSB,     99 Vcc LSB
//  100 TX_BIA MSB, 101 TX_BIA LSB,
//  102 TX MSB,     103 TX LSB,     104 RX MSB,     105 RX LSB
    
    FILE *focat;
    float temperature, vcc, tx_bias, optical_tx, optical_rx, RAW_tx, RAW_rx;
    char temp[10], vccc[10], txbi[10], optx[10], oprx[10], rwtx[30], rwrx[30];
    int i;
    //Open (or create) the csv file and write the heading row
    focat=fopen("fcatdata.csv", "w");
    
        if(focat == NULL)
        {
                printf("error openining file\n");
                exit(1);
        }
    fprintf(focat,"Temp, Vcc, Tx_Bias, Tx, Rx, RAWTx, RAWRx\n");
    fclose(focat);
    focat=fopen("fcatdata.csv", "a+");
    i=0;
    //start infinite loop
    for(;;) 
    {
    if(!read_eeprom(0x51));
    else exit(EXIT_FAILURE);
    i=i+1;

    //Taking MSB and LSB data and converting
    temperature =  (A51[96]+(float) A51[97]/256);
    vcc =                   (float)(A51[98]<<8  | A51[99])  * 0.0001;
    tx_bias =               (float)(A51[100]<<8 | A51[101]) * 0.002;
    optical_tx = 10 * log10((float)(A51[102]<<8 | A51[103]) * 0.0001);
    optical_rx = 10 * log10((float)(A51[104]<<8 | A51[105]) * 0.0001);
    
    RAW_tx =               ((float)(A51[102]<<8 | A51[103]) * 0.0001);
    RAW_rx =               ((float)(A51[104]<<8 | A51[105]) * 0.0001);
    
    //Display Diagnostics Monitoring Data in Terminal
    printf ("SFP Temperature = %4.4fC\n", temperature);
    printf ("Vcc, Internal supply = %4.4fV\n", vcc);
    printf ("TX bias current = %4.4fmA\n", tx_bias);
    printf ("Tx, Optical Power = %4.4f dBm", optical_tx);
    printf (", %6.6f mW\n", RAW_tx);
    printf ("Rx, Optical Power = %4.4f dBm", optical_rx);
    printf (", %6.6f mW\n", RAW_rx);
    printf ("iteration %d \n", i);
   
    //Change the integers into strings for appending to file
    sprintf(temp, "%4.4f", temperature);
    sprintf(vccc, "%4.4f", vcc);
    sprintf(txbi, "%4.4f", tx_bias);
    sprintf(optx, "%4.4f", optical_tx);
    sprintf(oprx, "%4.4f", optical_rx);
    sprintf(rwtx, "%6.6f", RAW_tx);
    sprintf(rwrx, "%6.6f", RAW_rx);
    
    //Appends DDM Data into a new row of a csv file
    //focat=fopen("fcatdata.csv", "a");
    fprintf(focat, "%s,%s,%s,%s,%s,%s,%s\n",temp,vccc,txbi,optx,oprx,rwtx,rwrx);
    //fclose(focat);

    }
        
    fclose(focat);
    return 0;
    }

When I have the code set up to open the .csv file prior to entering the loop I get the following error on the 1020th iteration:

SFP Temperature = 31.9258C

Vcc, Internal supply = 3.1374V

TX bias current = 8.0540mA

Tx, Optical Power = -1.8006 dBm, 0.660600 mW

Rx, Optical Power = -40.0000 dBm, 0.000100 mW

Unable to open I2C device: Too many open files

When i change the comments towards the bottom of the code so it reads as follows:

//Appends DDM Data into a new row of a csv file
focat=fopen("fcatdata.csv", "a");
fprintf(focat, "%s,%s,%s,%s,%s,%s,%s\n",temp,vccc,txbi,optx,oprx,rwtx,rwrx);
fclose(focat);

and then also comment out the file open prior to the loop, I am subsequently presented with the following fault on the 1021st loop iteration:

SFP Temperature = 31.8906C

Vcc, Internal supply = 3.1372V

TX bias current = 8.0620mA

Tx, Optical Power = -1.8006 dBm, 0.660600 mW

Rx, Optical Power = -40.0000 dBm, 0.000100 mW

Segmentation fault

I think this related somehow to ulimit - n showing a result of 1024 but i need to be able to run this script continuously for a week and therefore changing ulimit isnt a real solution for the problem.

I tested this theory by making a script which loops endlessly and appends the integer i to a csv file and that reached far beyond 1021 rows of data. This has been bothering me for a week now. Any help is appreciated.

Criticism on formatting etc it welcome, I don't often post here (or anywhere for that matter)


int read_eeprom(unsigned char address)
{
    int xio,i,fd1;
    xio = wiringPiI2CSetup (address);
    if (xio < 0) 
    {
        fprintf (stderr, "xio: Can't initialise I2C: %s\n",
                 strerror (errno));
        return 1;
    }
    for(i=0; i <128; i++) 
        {

        fd1 = wiringPiI2CReadReg8 (xio,i);
        if  (address == 0x50) 
            {
                A50[i] = fd1;
            }
        else 
            {
                A51[i] = fd1;
            }
        if (fd1 <0) 
            {
                fprintf (stderr, "xio: Can't read i2c address 0x%x: %s\n",
                         address, strerror (errno));
                return 1;
            }
        }
    return 0;
}

Edit 1: clarified the two scenarios where the file is opened and closed

Edit 2: added info on what is in read_eeprom

Edit 3: solved by adding close(fp); at the end of read_eeprom

Edit 4: solved properly by adding close(xio); at the end of read_eeprom - Credits to @JohnH


Solution

  • You need to only call wiringPiI2CSetup() once in the process. One way this can be achieved by using a static variable for xio so that it will retain the value between calls:

    int read_eeprom(unsigned char address)
    {
        int i, value;
        static int xio = -1;
    
        if( xio == -1 ) {
           xio = wiringPiI2CSetup (address);
           if (xio < 0) {
               fprintf (stderr, "xio: Can't initialise I2C: %s\n",
                        strerror (errno));
               return 1;
           }
        }
    
        for(i=0; i <128; i++) {
            value = wiringPiI2CReadReg8 (xio,i);
            if( value > 0 ) {
                if (address == 0x50)
                    A50[i] = value;
                else 
                    A51[i] = value;
            }
            else {
                fprintf (stderr, "xio: Can't read i2c address 0x%x: %s\n",
                         address, strerror (errno));
                return 1;
            }
        }
        return 0;
    }
    

    Another way would be to call wiringPiI2CSetup() each time you enter the routune, but then to close it between each call:

    int read_eeprom(unsigned char address)
    {
        int xio, i, value;
        xio = wiringPiI2CSetup (address);
        if (xio < 0) {
           fprintf (stderr, "xio: Can't initialise I2C: %s\n",
                    strerror (errno));
           return 1;
        }
    
        for(i=0; i <128; i++) {
            value = wiringPiI2CReadReg8 (xio,i);
            if( value > 0 ) {
                if (address == 0x50)
                    A50[i] = value;
                else 
                    A51[i] = value;
            }
            else {
                fprintf (stderr, "xio: Can't read i2c address 0x%x: %s\n",
                         address, strerror (errno));
                close(xio);
                return 1;
            }
        }
        close(xio);
        return 0;
    }