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!