Problem in function with writing file in SD (other part it works)

Hello,
I am currently working in a project for measuring O2 and temperature of water. I am using

my issue comes from the calibration of the O2, I have a small bug somewhere but i can not find what is the issue!
for information:
I am writing another file in another section of the complete script, this one is writed correctly.
the prints on the serial monitor of the values to save to the file are correct, the issue is with the saving file...
in serial monitor I am getting:

writing temp 20.87
writing Voltage 1238.18
Error opening file for writing!

here is the function with the declaration of variables:

// sudo chmod a+rw /dev/ttyACM0

//##################//
///// libraries //////
//##################//
// -SD reader
#include <SPI.h>
#include <SD.h>

// -Adafruid shield
#include <Adafruit_GFX.h>
#include <Adafruit_ST7735.h>
#include <Adafruit_seesaw.h>
#include <Adafruit_TFTShield18.h>


// -Temp measurement
#include "max6675.h"

// -O2 measurement
#include <Arduino.h>

//##################//
/// end libraries ////
//##################//

//##################//
///// variables //////
//##################//

//shield variables
Adafruit_TFTShield18 ss;
uint32_t buttons;
/////physical pins
#define SD_CS 4                                                  // Chip select line for SD card on Shield
#define TFT_CS 10                                                // Chip select line for TFT display on Shield
#define TFT_DC 8                                                 // Data/command line for TFT on Shield
#define TFT_RST -1                                               // Reset line for TFT is handled by seesaw!
Adafruit_ST7735 tft = Adafruit_ST7735(TFT_CS, TFT_DC, TFT_RST);  // Using hardware SPI






/////////////////////////////
// temp read ////////////////
/////////////////////////////
/////physical pins
#define pinSCK 36  // SHOULD NOT use the SPI hardware! so not pin 53 https://www.arduino.cc/reference/en/language/functions/communication/spi/
#define pinCS 37   // SHOULD NOT use the SPI hardware! so not pin 52 https://www.arduino.cc/reference/en/language/functions/communication/spi/
#define pinSO 38   // randomly chosen
MAX6675 thermocouple(pinSCK, pinCS, pinSO);
const uint32_t minTimeMax6675 = 220;  // min time in miliseconds for correct (temperature) measurement with the max6675
#define tempColor ST77XX_RED
#define tempColorFont ST77XX_WHITE
#define tempColorCursor ST77XX_GREEN


/////////////////////////////
// pH read //////////////////
/////////////////////////////
const uint32_t minTimePh = 0;  // min time in miliseconds for correct (pH) measurement
#define phColor ST77XX_GREEN
#define phColorFont ST77XX_BLACK
#define phColorCursor ST77XX_BLUE





/////////////////////////////
// O2 read //////////////////
/////////////////////////////
const uint32_t minTimeO2 = 220;  // min time in miliseconds for correct (pH) measurement
/////physical pins
const int analogPinO2 = A1;  // Assigning A0 to the variable
/////calibration parameters
#define VREF 5000     //VREF(mv)
#define ADC_RES 1024  //ADC Resolution

/////calibration O2 values
const uint16_t DO_Table[41] = {
  14460, 14220, 13820, 13440, 13090, 12740, 12420, 12110, 11810, 11530,
  11260, 11010, 10770, 10530, 10300, 10080, 9860, 9660, 9460, 9270,
  9080, 8900, 8730, 8570, 8410, 8250, 8110, 7960, 7820, 7690,
  7560, 7430, 7300, 7180, 7070, 6950, 6840, 6730, 6630, 6530, 6410
};
bool oneOrTwoPointCall = true;
float T1 = 25;
float T2 = 35;

//CAL1 High temperature point, CAL2 Low temperature point
float CAL1_V;  //mv
float CAL1_T;  //℃
//Two-point calibration needs to be filled CAL2_V and CAL2_T
float CAL2_V;  //mv
float CAL2_T;  //℃

File myFileO2Cal;                    // file object
String calibrationO2File = "O2cal";  // file name for storing data needs to be less than 8 chars!!!!!

#define o2Color ST77XX_BLUE
#define o2ColorFont ST77XX_WHITE
#define o2ColorCursor ST77XX_GREEN





/////////////////////////////
// conduct read /////////////
/////////////////////////////
const uint32_t minTimeConduct = 220;  // min time in miliseconds for correct (pH) measurement
#define conducColor ST77XX_YELLOW
#define conducColorFont ST77XX_BLACK
#define conducColorCursor ST77XX_BLUE








//file definition values
String fileName = "run";  // file name for storing data needs to be less than 8 chars!!!!!
int fileNumber = 0;       // file index for fileName
File myFile;              // file object
bool writeFile = 1;       // file object


// time variables
uint32_t previousTime = 0;  // variable to save previous time to ensure that the measurement takes enought time
uint32_t initialTime = 0;   // variable to save original time when the measurement began



//display configuration
const int numberOfMeasurements = 4;                                                                  // number of different displays
String titles[numberOfMeasurements] = { "Temp", "pH", "O2", "Conduct" };                             // titles for each measurement
uint32_t minTimes[numberOfMeasurements] = { minTimeMax6675, minTimePh, minTimeO2, minTimeConduct };  // min times list for each measurement
uint32_t minTime;                                                                                    // max(minTimes)

int currrentDisplayPosition = 0;                                                                                       // current position for display
int NotcurrrentDisplayPosition = 1;                                                                                    // the position that is not displayed
uint16_t backGroundColor[numberOfMeasurements] = { tempColor, phColor, o2Color, conducColor };                         // background color for display depending on chosen display
uint16_t fontColor[numberOfMeasurements] = { tempColorFont, phColorFont, o2ColorFont, conducColorFont };               // font color for display depending on chosen display
uint16_t pointDispColor[numberOfMeasurements] = { tempColorCursor, phColorCursor, o2ColorCursor, conducColorCursor };  // point color for display depending on chosen display


//display values

//Y axis temp
int AxisMax[numberOfMeasurements] = { 35, 8 };  // maximum value of the Y axis for the measurement that is displayed (temperature or pH)
int AxisMin[numberOfMeasurements] = { 22, 5 };  // minumum value of the Y axis for the measurement that is displayed (temperature or pH)

//X axis tile
float AxisTimeMin = 0;                        // maximum value of the X axis (time)
float AxisTimeMax = 1;                        // minimum value of the X axis (time)
float timeRange = AxisTimeMax - AxisTimeMin;  // range of time before needing to update the display
int numberOfPathsOverAxis = 0;                // number of times that the display went thought the X axis

//X axis pixel definition
const int minPixelX = 18;   // position of the pixel in X for the begining of the axis
const int maxPixelX = 139;  // position of the pixel in X for the end of the axis

//y axis pixel definition
const int maxPixelY = 95;  // position of the pixel in Y for the begining of the axis
const int minPixelY = 23;  // position of the pixel in Y for the end of the axis



//measurement calculation
float averageTimeForValue = 1000;  // time interval to return a measurement value in miliseconds
uint32_t lastTimeForValue = 0;     // time counter since last returned measurement value

float sumTime = 0;                        // sum of time values in time interval of averageTimeForValue
float sumMeasures[numberOfMeasurements];  // sum of measurements values in time interval of averageTimeForValue


int counterPoints = 0;        // counter of numbers of points measured in a time interval of averageTimeForValue
int counterStoredPoints = 0;  // counter of numbers of stored measurements points
int counterOfPixel = 0;       // counter of numbers of pixels used (to check if display should be updated)

const int maxNumberOfPoints = maxPixelX - minPixelX;                                         // max numbers of possible pixels in the X axis before going out of the drawn axis
float timeStepForDrawing = ((AxisTimeMax - AxisTimeMin) * 3600 * 1000) / maxNumberOfPoints;  // time step for drawing next pixel
uint32_t previousDrawPoint = 0;                                                              // value of the last Y (in pixels) for the last measured point displayed

float storedMeasures[numberOfMeasurements][maxNumberOfPoints];       // saving values for each measurement
float storedTime[maxNumberOfPoints];                                 // saving time values of each measurement done (ie., storedMeasures)
float storedPixelMeasures[numberOfMeasurements][maxNumberOfPoints];  // saving a pixel positions of the measurements for cases when changing the displayed measurement
float storedPixelTime[maxNumberOfPoints];                            // saving a pixel position of the time where the measurement was done (ie., storedPixelMeasures)




void calibrateO2() {
  Serial.println("calibration of O2 sensor");
  int setFirstValue = 1;
  int setSecondValue = 1;
  currrentDisplayPosition = 2;
  bool averageValue = false;
  bool constantTemp = false;

  float sum = 0;
  float average;
  uint32_t valueVO2;  // variable to store measured data
  String filename_ = (calibrationO2File + ".csv").c_str();
  float averageT;
  float sumT = 0;
  uint32_t valueT;  // variable to store measured data
  tft.fillScreen(backGroundColor[currrentDisplayPosition]);
  tft.setTextSize(1);
  tft.setTextColor(fontColor[currrentDisplayPosition]);

  tft.setCursor(0, 0);
  tft.print("Calibration for O2\n (skip)<=      =>(calibration)");



  while (true) {
    buttons = ss.readButtons();
    if (!(buttons & TFTSHIELD_BUTTON_RIGHT) || !(buttons & TFTSHIELD_BUTTON_UP)) {  // ( => ) Assuming LOW means pressed
      SD.remove(filename_);

      tft.fillScreen(backGroundColor[currrentDisplayPosition]);
      tft.setCursor(10, 10);
      tft.print("Put O2 probe in a " + String(T1));
      tft.write(247);
      tft.print("C O2 saturated solution when the voltage measure stabilizes the calibration will continue");
      if (!(buttons & TFTSHIELD_BUTTON_RIGHT)) {
        constantTemp = true;
      } else if (!(buttons & TFTSHIELD_BUTTON_UP)) {
        tft.print(" the temperature can be different as it will be measured using thermocouple");
      }
      while (true) {
        buttons = ss.readButtons();
        // reading measurement
        valueVO2 = analogRead(analogPinO2) * (VREF / ADC_RES);
        sum -= storedMeasures[currrentDisplayPosition][counterStoredPoints];
        storedMeasures[currrentDisplayPosition][counterStoredPoints] = valueVO2;
        sum += valueVO2;
        if (constantTemp) {
          valueT = T1;
        } else {
          valueT = thermocouple.readCelsius();
        }
        sumT -= storedMeasures[0][counterStoredPoints];
        storedMeasures[0][counterStoredPoints] = valueT;
        sumT += valueT;

        counterStoredPoints = (counterStoredPoints + 1) % maxNumberOfPoints;
        if (counterStoredPoints == 0 && !averageValue) {
          averageValue = true;
        }


        // Serial.println("valueVO2:\t" + String(valueVO2) + "\tVoltage(mv)" + String(valueVO2 * VREF / ADC_RES));
        tft.setCursor(15, 100);
        tft.fillRect(85, 100, 45, 8, backGroundColor[currrentDisplayPosition]);
        tft.print("Voltage(mv) " + String(valueVO2));
        tft.setCursor(15, 110);
        if (constantTemp) {
          tft.print("Temperature(C) " + String(valueT) + " CONST.");
        } else {
          tft.fillRect(105, 110, 45, 8, backGroundColor[currrentDisplayPosition]);
          tft.print("Temperature(C) " + String(valueT));
        }




        if (averageValue) {
          average = sum / maxNumberOfPoints;

          Serial.println("average\t" + String(average));
          Serial.println("relError\t" + String((abs(valueVO2 - average) / average)));
          if ((abs(valueVO2 - average) / average) <= 0.01) {
            averageT = sumT / maxNumberOfPoints;
            tft.fillScreen(backGroundColor[currrentDisplayPosition]);
            tft.setCursor(10, 10);
            tft.print("First value for O2 probe saved.");
            Serial.println("calibration O2 file open");
            Serial.println("writing temp " + String(averageT));
            Serial.println("writing Voltage " + String(average));
            myFileO2Cal = SD.open(filename_, FILE_WRITE);
            if (!myFileO2Cal) {
              Serial.println("Error opening file for writing!");
              return;
            }
            // myFileO2Cal.print(String(averageT));
            myFileO2Cal.print(String(T1));
            myFileO2Cal.print(";");
            // myFileO2Cal.print(String(average));
            myFileO2Cal.print(String(10));
            myFileO2Cal.println(";");
            myFileO2Cal.close();
            Serial.println("calibration O2 file closed");

            // reseting the stored data
            for (int i = 0; i < maxNumberOfPoints; i++) {
              storedMeasures[currrentDisplayPosition][i] = 0;
            }
            sum = 0;
            counterStoredPoints = 0;
            averageValue = false;


            delay(5000);
            break;
          }
        }
        delay(minTimeO2);
      }
      break;
    } else if (!(buttons & TFTSHIELD_BUTTON_LEFT)) {
      setFirstValue = 0;
      delay(1000);
      break;
    }
  }
  if (setFirstValue == 1) {
    tft.fillScreen(backGroundColor[currrentDisplayPosition]);
    tft.setTextSize(1);
    tft.setTextColor(fontColor[currrentDisplayPosition]);

    tft.setCursor(0, 0);
    tft.print("Calibration for O2\n (skip second point)<=      =>(second point (temperature correction))");



    while (true) {
      buttons = ss.readButtons();
      if (!(buttons & TFTSHIELD_BUTTON_RIGHT) || !(buttons & TFTSHIELD_BUTTON_UP)) {  // ( => ) Assuming LOW means pressed
        tft.fillScreen(backGroundColor[currrentDisplayPosition]);
        tft.setCursor(10, 10);
        tft.print("Put O2 probe in a " + String(T2));
        tft.write(247);
        tft.print("C O2 saturated solution when the voltage measure stabilizes the calibration will continue");
        if (!constantTemp) {
          tft.print(" the temperature can be different as it will be measured using thermocouple");
        }
        while (true) {
          buttons = ss.readButtons();
          // reading measurement
          valueVO2 = analogRead(analogPinO2) * (VREF / ADC_RES);
          sum -= storedMeasures[currrentDisplayPosition][counterStoredPoints];
          storedMeasures[currrentDisplayPosition][counterStoredPoints] = valueVO2;
          sum += valueVO2;

          if (constantTemp) {
            valueT = T2;
          } else {
            valueT = thermocouple.readCelsius();
          }
          sumT -= storedMeasures[0][counterStoredPoints];
          storedMeasures[0][counterStoredPoints] = valueT;
          sumT += valueT;

          counterStoredPoints = (counterStoredPoints + 1) % maxNumberOfPoints;
          if (counterStoredPoints == 0 && !averageValue) {
            averageValue = true;
          }


          // Serial.println("valueVO2:\t" + String(valueVO2) + "\tVoltage(mv)" + String(valueVO2 * VREF / ADC_RES));
          tft.setCursor(15, 100);
          tft.fillRect(85, 100, 45, 8, backGroundColor[currrentDisplayPosition]);
          tft.print("Voltage(mv) " + String(valueVO2));
          tft.setCursor(15, 110);
          if (constantTemp) {
            tft.print("Temperature(C) " + String(valueT) + " CONST.");
          } else {
            tft.fillRect(105, 110, 45, 8, backGroundColor[currrentDisplayPosition]);
            tft.print("Temperature(C) " + String(valueT));
          }




          if (averageValue) {
            average = sum / maxNumberOfPoints;

            Serial.println("average\t" + String(average));
            Serial.println("relError\t" + String((abs(valueVO2 - average) / average)));
            if ((abs(valueVO2 - average) / average) <= 0.01) {
              averageT = sumT / maxNumberOfPoints;
              tft.fillScreen(backGroundColor[currrentDisplayPosition]);
              tft.setCursor(10, 10);
              tft.print("First value for O2 probe saved.");
              Serial.println("calibration O2 file open");
              myFileO2Cal = SD.open(filename_, FILE_WRITE);
              if (!myFileO2Cal) {
                Serial.println("Error opening file for writing!");
                return;
              }
              Serial.println("writing temp " + String(averageT));
              myFileO2Cal.print(String(averageT));
              myFileO2Cal.print(";");
              Serial.println("writing Voltage " + String(average));
              myFileO2Cal.print(String(average));
              myFileO2Cal.println(";");
              myFileO2Cal.flush();
              myFileO2Cal.close();
              Serial.println("calibration O2 file closed");

              // reseting the stored data
              for (int i = 0; i < maxNumberOfPoints; i++) {
                storedMeasures[currrentDisplayPosition][i] = 0;
              }
              sum = 0;
              counterStoredPoints = 0;
              averageValue = false;


              delay(5000);
              break;
            }
          }
          delay(minTimeO2);
        }
        break;
      } else if (!(buttons & TFTSHIELD_BUTTON_LEFT)) {
        setFirstValue = 0;
        delay(1000);
        break;
      }
    }
  } else {
    tft.fillScreen(backGroundColor[currrentDisplayPosition]);
    tft.setCursor(10, 10);
    tft.print("Single value calibration finished (without temperature correction).");
    delay(2500);
  }
  if (setSecondValue == 1) {
    tft.fillScreen(backGroundColor[currrentDisplayPosition]);
    tft.setCursor(10, 10);
    tft.print("Double value calibration finished (with temperature correction).");
    delay(2500);
  }
}
void setup() {}
void loop() {}

any input would be appreciated, as it is driving me crazy!

It would be easier to find the error if you posted a sketch that actually did anything. That could plausibly have written anything to the serial monitor.

I see you are using Strings. What Arduino board is this running on? What is the compiler's report about how much memory your sketch is taking? Do you know this does not necessarily account for whatever your display requires?

You might also post the code that worked before you added stuff to it to make it not work.

Move away from Strings. Write a version that does not (yet) use any display.

a7

hello!
thanks for the reply.

It would be easier to find the error if you posted a sketch that actually did anything. That could plausibly have written anything to the serial monitor.

I removed everything else so it is a little bit more simple, the issue is in the funciton as it is executed (as the serial are being printed) and the tft screen updated...

I see you are using Strings. What Arduino board is this running on? What is the compiler's report about how much memory your sketch is taking? Do you know this does not necessarily account for whatever your display requires?

I am using the arduino mega and almost all the memory is used:

Sketch uses 44636 bytes (17%) of program storage space. Maximum is 253952 bytes.
Global variables use 7744 bytes (94%) of dynamic memory, leaving 448 bytes for local variables. Maximum is 8192 bytes.

Move away from Strings.

could you be more specific? how would i then define the name of the file (in the global variables) if not with a string? (ie., String calibrationO2File = "O2cal"; )

thanks!

There is no code to execute in your posted sketch, both setup and loop are empty. NONE of that other code will be used.

Capital S Strings are a convenience, but can cause memory issues.

Using C character arrays and small-s string functions is preferable.

Indeed. That would be risky .I don't know how much trouble it would be, but my suspicion is that if you were to try using the serial monitor instead of the display, and small-s C strings it would work.

a7

1 Like

thanks, I am new to this limitations of memory... i am using the display in the final project so, i need to keep this but, will check about the character arrays and small-s string functions! will come back when i have the time to test it

Hello @alto777 ,
thanks for your input! and you are right, when i changed the "uint32_t" to "uint16_t" of these variables:

uint16_t storedMeasures[numberOfMeasurements][maxNumberOfPoints];     
uint16_t storedTime[maxNumberOfPoints];                               
uint16_t storedPixelMeasures[numberOfMeasurements][maxNumberOfPoints];
uint16_t storedPixelTime[maxNumberOfPoints];                          

it solved the issue! (i tested with uint32_t and defining const char* calibrationO2File = "O2cal.csv";) but without luck.... now my issue is that I need to store the float values (so i need to use uint32_t or float.... ) would you have any input in how can i improve this? (i am also blocked to the mega as the display board is using all the uno pins.....

Yay!

Do put getting along without capital S Strings on your list for learning time. Dealing with arrays of all kinds, and pointers too, is essential to wringing the maximum out of limited resources.

Arrays and pointers are done with great beauty in C. Character arrays are supported by a wide variety of functions and are a much better fit for small machines. After they aren't hard, they are easier, though I will admit that Strings are convenient and very easier to use. But they can make you pay for that. 'Nuff said.

I can't your code just now, but is this in fact true? I grew up with microprocessors of limited resources, both speed and storage. I just could not use floating point - touching it would make things explode quickly.

A vast wealth of techniques for handling real numbers using integers variously is out there. It usually requires knowing quite a bit about the range of numbers you deal with and the necessary resolution for useful results.

Floating point is a luxury, or was, or is if you don't have the memory or time for something massive and complicate.

Google

fixed point math

a7

1 Like

Not solving your problems.

The better way is to add yourself to the dialout group; your Linux distribution might have another name. See e.g. usb - How do I allow non-root access to /ttyUSB0? - Ask Ubuntu.

1 Like

Lol, this is the perfect situation to use the meme 'columbus i came for copper and found gold' thanks for that input! I will check this out :slight_smile:

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.