I'm slowly learning Arduino, trying different sensors with the aim of combining them into a Reverse Osmosis control system. I'm coding for a Uno but eventually moving to a Mega for more outputs when all sensors and outputs are connected and coded to one Control System.
So far I've managed to set up a flow sensor (YF‐ S201) to count flow and total volume(volume). These are float values. This prints to a 16x2 screen. The float value saves to a SD card in a text file when the total changes.
I've used a data.File.println(volume); or print(volume); print(",");
to the SD card to save as a line or comma separated within the txt file.
My struggle is how to recall this total in the event of a power cut or restart. I'm thinking i want to take the float value saved to the SD card and add it to setup.
total = some code to recall the SD card info and ensure its readable as a float value this should give a starting point for the sensor to then add on top off.
I have hunted through google for anyone doing something similar to what im doing but cant find anything. Does anyone have an idea of whether this is possible and how?
Hi i can do either add to the end or replace. whichever would have the best outcome. I would like to data log with time all changes but that could be written to a seperate txt file.
I don't know the code to do these steps ... the good news it sound like what im trying to do will work
go to the end of the file
walk your way back to find the start of the last line (if you used println()) or the last separator (',').
read a float from the character after the separator
#include <SoftwareSerial.h>
#include <LiquidCrystal_I2C.h>
#include <SPI.h>
#include <SD.h>
SoftwareSerial mySerial(7, 6); // RX, TX
unsigned char data[4] = {};
float distance;
float initialDistance = 120;
float offset;
float surfaceArea = 8525;
float heightOfWater;
float tankLvl; // sensor level in water tank
float tankLvlCheck; // old sensor level value in water tank
bool tankLvlChanged; // yes or no if match
volatile int flow_frequency; // Measures flow sensor pulses
// Calculated litres/hour
float totalizer = 0.0, l_minute;
float totalizerCheck;
bool totalizerChanged;
unsigned char flowsensor = 2; // Sensor Input
const int chipSelect = 4;
unsigned long currentTime;
unsigned long cloopTime;
float frequency = 1.1056;
LiquidCrystal_I2C lcd(0x27, 16, 2); // set the LCD address to 0x27 for a 16 chars and 2 line display
void flow () // Interrupt function
{
flow_frequency++;
}
void setup()
{
pinMode(flowsensor, INPUT);
digitalWrite(flowsensor, HIGH); // Optional Internal Pull-Up
Serial.begin(57600);
mySerial.begin(9600);
lcd.init();
lcd.backlight();
attachInterrupt(digitalPinToInterrupt(flowsensor), flow, RISING); // Setup Interrupt
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Wtr Flow/Tnk Lvl");
lcd.setCursor(0, 1);
lcd.print("Circuit Digest");
currentTime = millis();
cloopTime = currentTime;
Serial.print("Initializing SD card...");
// see if the card is present and can be initialized:
if (!SD.begin(chipSelect)) {
Serial.println("Card failed, or not present");
// don't do anything more:
while (1);
}
Serial.println("card initialized.");
}
void loop()
{
currentTime = millis();
// Every second, calculate and print litres/hour
if (currentTime >= (cloopTime + 1000))
{
cloopTime = currentTime; // Updates cloopTime
if (flow_frequency != 0) {
// Pulse frequency (Hz) = 7.5Q, Q is flow rate in L/min.
l_minute = ((flow_frequency / 7.5) * frequency); // (Pulse frequency x 60 min) / 7.5Q = flowrate in L/hour
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Rate");
lcd.print(l_minute);
lcd.print(" L/M");
l_minute = l_minute / 60;
lcd.setCursor(8, 0);
totalizer = totalizer + l_minute;
lcd.print("Vol");
lcd.print(totalizer);
lcd.print("L");
flow_frequency = 0; // Reset Counter
Serial.print(l_minute, DEC); // Print litres/hour
Serial.println(" L/Sec");
lcd.setCursor(0, 1);
lcd.print(tankLvl);
lcd.print("Litres");
}
else {
Serial.println(" flow rate = 0 ");
lcd.clear();
lcd.setCursor(0, 0);
lcd.print("Rate:");
lcd.print( flow_frequency );
lcd.print(" L/M");
lcd.setCursor(8, 0);
lcd.print("Vol:");
lcd.print(totalizer);
lcd.print("L");
lcd.setCursor(0, 1);
lcd.print(tankLvl);
lcd.print("Litres");
}
}
do {
for (int i = 0; i < 4; i++)
{
data[i] = mySerial.read();
}
} while (mySerial.read() == 0xff);
mySerial.flush();
if (data[0] == 0xff)
{
int sum;
sum = (data[0] + data[1] + data[2]) & 0x00FF;
if (sum == data[3])
{
distance = (data[1] << 8) + data[2];
if (distance > 30)
{
Serial.print("distance=");
Serial.print(distance / 10);
Serial.println("cm");
distance = (distance / 10) - 3;
delay(2000);
Serial.println(distance);
heightOfWater = initialDistance - distance;
tankLvl = (heightOfWater * surfaceArea) / 1000;
Serial.println(heightOfWater);
delay(2000);
Serial.print("volume=");
Serial.println(tankLvl);
lcd.setCursor(0, 1);
lcd.print(tankLvl);
lcd.print("Litres");
} else
{
Serial.println("Below the lower limit");
}
} else Serial.println("ERROR");
}
{
delay(100);
}
if (tankLvl != tankLvlCheck)
{ tankLvlChanged = true;
tankLvlCheck = tankLvl;}
else if (tankLvl == tankLvlCheck){
tankLvlChanged = false;}
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
File dataFile = SD.open("lvlChng.txt", FILE_WRITE);
// if the file is available, write to it:
if (dataFile && tankLvlChanged == true) {
dataFile.println(tankLvl);
dataFile.close();
// print to the serial port too:
Serial.print("Data Saved");
}
else {
dataFile.close();
}
if (totalizer != totalizerCheck)
{ totalizerChanged == true;
totalizerCheck = totalizer;
}
else if (totalizer == totalizerCheck){
totalizerChanged = false;}
// open the file. note that only one file can be open at a time,
// so you have to close this one before opening another.
dataFile = SD.open("flowTot.txt", FILE_WRITE);
// if the file is available, write to it:
if (dataFile && totalizerChanged == true) {
dataFile.println(totalizer);
dataFile.close();
// print to the serial port too:
Serial.print("Data Saved");
}
else {
dataFile.close();
}
}
I also need to create functions with the instructions for those functions inside to neaten up and make it easier to read. Something i need to work out as last time i tried it caused the code to not work correctly.
Hi peeps. I'm quite new to this and still struggling to achieve what i set out to do. While I understand the principle behind J-M-L Jacksons advice. I do not know the code i need to implement to float recall from the SD card.
Ok so far in my attempt i have created a text file that deletes itself and then saves the new data. So it will always contain only the most recent total
void loop()
SD.remove("TotTemp.txt");
File dataFileMem = SD.open("TotTemp.txt", FILE_WRITE);
// if the file is available, write to it:
if (dataFileMem == true) {
dataFileMem.println(totalizer);
dataFileMem.close();
// print to the serial port too:
Serial.print("Tot Temp Data Saved");
}
else {
dataFileMem.close();
}
My next step is to read that and turn the characters into a float value. I have attempted this by copying some code online and adapting but i dont think its right. this code goes at the end of void setup()
// TEST READ SD FILE TO FLOAT
{ SD.open("TotTemp.txt", FILE_WRITE);
//dataFileMem.seek(0);
if (dataFileMem.available())
{
Serial.print("SD reaad and Available");
char aChar = dataFileMem.read();
if (aChar != '\n' && aChar != '\r')
{
fileContents[index++] = aChar;
fileContents[index] = '\0'; // NULL terminate the array
}
else // the character is CR or LF
{
Serial.print("fileContents: [");
Serial.print(fileContents);
Serial.println("]");
if (strlen(fileContents) > 0)
{
float aVal = atof(fileContents);
aVal = totalizer;
// Do something with aVal
}
fileContents[0] = '\0';
index = 0;
dataFileMem.close();
}
}
if you want to read the file, it's probably logic to open it with FILE_READ rather than FILE_WRITE
you call SD.open but you don't have a variable initialised to play with the file. Look at the previous code, you were doing File dataFileMem = SD.open("TotTemp.txt", FILE_WRITE); and then you could use dataFileMem to access the file.
assuming dataFileMem is initialized, when you do
if (dataFileMem.available()) {
Serial.print("SD reaad and Available");
char aChar = dataFileMem.read();
if (aChar != '\n' && aChar != '\r')
{
fileContents[index++] = aChar;
fileContents[index] = '\0'; // NULL terminate the array
}
you only read ONE char from the file. that's not enough to represent your float. You could use a while / for loop to go over each character in file until you reach the end of the line and store them into the fileContents array which then you can indeed (after adding the trailing '\0' as you do) convert to a float. strtod() is better than atof() as it will let you catch errors
Thank you for your input. I had tried the code as was. Tried changing and removing things. forgot to put back
File dataFileMem =
I think I also changed the while to an if somewhere along the line but that's not understanding the code. Thank you for explaining that it runs through until it reaches the end of the line.
Do i need to specifically add the \0 into the write section of code?
ive also just tried changing atof() to strtod() and im getting a new error...
15_4_Ultrasonic___Flow_____SD_save:100:41: error: too few arguments to function 'double strtod(const char*, char**)'
float aVal = strtod(fileContents);
^
In file included from C:\Program Files (x86)\Arduino\hardware\arduino\avr\cores\arduino/Arduino.h:23:0,
from sketch\15_4_Ultrasonic___Flow_____SD_save.ino.cpp:1:
c:\program files (x86)\arduino\hardware\tools\avr\avr\include\stdlib.h:350:15: note: declared here
extern double strtod(const char __nptr, char __endptr);
^~~~~~
exit status 1
too few arguments to function 'double strtod(const char, char)'