Adafruit RTClib TimeSpan calculation fails on ESP32 - c++

I'm currently trying to measure remaining time until a given timestamp using Adafruit's RTClib library. My RTC module is a DS3231.
here is my TimeSpan object in code:
TimeSpan remaining = next.depart - rtc.now();
However when i try to print the remaining minutes i get no data.
Here is my code that prints the output:
Serial.println(String("current time: " + rtc.now().timestamp()));
Serial.println(String("targetTime: " + next.depart.timestamp()));
Serial.println(String("remaining time: " + remaining.minutes()));
And the output is right except for the remaining time in minutes:
current time: 2020-12-06T05:38:55
target time: 2020-12-06T05:42:30
aining time:
Notice that the last line is cut off in the serial output and the remaining minutes aren't displayed. current time and target time are both correct in the readout.
I can't perform any operations with the remaining time either:
if(remaining.minutes() >= 10)
In this case the condition is never met.
Am i missing something?

Your line of code:
Serial.println(String("remaining time: " + remaining.minutes()));
is not doing what you think it's doing.
remaining.minutes() returns type int8_t not type String. You're adding it to a C character pointer to the C string remaining time: - if the value is greater than the length of that string then the resulting pointer is invalid and you're lucky your program doesn't crash.
For instance, if remaining.minutes() were 3 then your output would be:
aining time:
Instead your code should look more like this:
Serial.println("remaining time: " + String(remaining.minutes()));
or better:
Serial.print("remaining time: ");
Serial.println(remaining.minutes());
The second form has the benefit of avoiding unnecessary String object instantiations and allocating memory.
The reason your two lines:
Serial.println(String("current time: " + rtc.now().timestamp()));
Serial.println(String("targetTime: " + next.depart.timestamp()));
work is that the timestamp() method returns a String, so adding a C character string to it results in string concatenation rather than adding an integer to a character pointer.
In these two cases your enclosing call to String() is superfluous and should be avoided:
Serial.println("current time: " + rtc.now().timestamp());
Serial.println("targetTime: " + next.depart.timestamp());
You're already computing String values; there's no need to make new String objects from them.
Find out what value remaining.minutes() is in order to answer your other question. It's likely an issue with the way remaining is computed, and is a matter for different question.

Related

Does substr change the position where the find function starts searching?

Does substr change the position where the find function starts searching ?
I have a char * named search_text containing the following text:
ABC_NAME = 'XYZSomeone' AND ABC_CLASS = 'XYZSomething'
I want to display the "ABC_NAME" value from that string.
Here is what I am doing:
std::cout << std::string(search_text).substr ( 12, std::string( search_text ).find ("'", 13 )-1) << std::endl;
My logic in the above in the substr is as follows:
The ABC_NAME value always begins at the 12th character, so start the substring there.
Do a find for the character ' (single quotation mark) from the 13th character onwards, starting from the 13th character (the second argument of the find() function). The resulting number will be the outer bound of the substr.
However, my code prints out the following:
XYZSomeone' AND ABC_C
However, when I try to display the value of the find() function directly, I do get the correct number for the location of the second ' (single quotation mark)
std::cout << std::string( search_text ).find ("'", 13 ) << std::endl;
This prints out:
22
So why is it that the substr is not finding the value of 22 as its second argument ?
It's a rather simple matter to evaluate your expression by hand, seeing how you already verified the result of find:
std::string(search_text).substr ( 12, std::string( search_text ).find ("'", 13 )-1)
std::string("ABC_NAME = 'XYZSomeone' AND ABC_CLASS = 'XYZSomething'").substr ( 12, 22-1)
Now check the documentation for substr: "Returns a substring [pos, pos+count)". The character at position 12 is the 'X' for the name portion, and the character at position 12+21 = 33 is the 'L' from the class portion. So we expect the substring starting at that 'X' and going up to just before that 'L', which is "XYZSomeone' AND ABC_C". Check.
(It is understandable to forget whether substr takes a length or a position at which to end. Different languages do disagree on this. Hence the link to the documentation.)
Unsolicited commentary
Trying to do so much in one line makes your code harder to read and harder to debug. In this case, it also hurts performance. There is no need to convert search_text to a std::string twice.
std::string search_string{search_text};
std::size_t found = search_string.find('\'', 12);
if ( found != std::string::npos )
found -= 12;
std::cout << search_string.substr(12, found) << std::endl;
This cuts the number of times a string is constructed (hence the times the string data is copied) from three to two.
If you are using C++17, you can improve the performance even more by constructing no strings. Just use std::string_view instead of std::string. For this scenario, it has the same member functions taking the same parameters; all you have to change is the type of search_string. This puts the performance on par with C code.
Even better: since string views are so cheap to create, you could even write your code – without a performance hit – so that it doesn't matter whether substr takes a length or takes the past-the-end position.
std::string_view search_string{search_text};
std::string_view ltrimmed = search_string.substr(12);
std::size_t found = ltrimmed.find('\'');
std::cout << ltrimmed.substr(0, found) << std::endl;
Constructive laziness FTW!

Trouble with output function C++

I am having trouble getting this program to output properly. It simulates a drunken sailor on a board that randomly goes one step to the left or right. At the end of the simulation, the program outputs the percentage of times he fell off the board vs not falling off. My percentage is always zero, and I can't figure out whats wrong with my code.
This function correctly outputs the "experiments" and "fallCount" variable, but always displays "fallCount / experiments" as zero.
This should read "After 2 experiments, sailor fell 1 time, fall percentage was 0.5%"
(if experiments = 2 and fallCount = 1) instead, its 0% every time.
Let me know what I am doing wrong. Thank you!
void outputExperimentStats(int experiments, int fallCount)
{
cout << "After " << experiments << " experiments, sailor fell "
<< fallCount << " time, fall percentage was " << fallCount / experiments << "%\n";
}
That is because you are using integer division. There are no decimals, so things get truncated. E.g.
1 / 2 --> 0 // integer division
This is correct, and expected behavior.
To get the behavior you want, use double or float.
1.0 / 2.0 --> 0.5 // double division
In your example, you can either change the types of your inputs to double or if you want to keep them int, you can convert them during the division
static_cast<double>(fallCount) / static_cast<double>(experiments)

Can you put breaks in a string literal?

This is a constant I am using, it is split like this because I did not want to have to scroll my editor
const string PROGRAM_DESCRIPTION = "Program will calculate the amount "
"accumulated every month you save, until you reach your goal.";
int main()
{
cout << PROGRAM_DESCRIPTION;
return 0;
}
Currently prints out in command prompt as
Program will calculate the amount accumulated every month you save,until you re
ach your goal.
When this prints out I would like it to print on two separate lines like below...
Program will calculate the amount accumulated every month you save,
until you reach your goal.
I do not know where to put break statements in a string so I can get it to print out correctly.
Just insert a \n character to force a new line
const string PROGRAM_DESCRIPTION = "Program will calculate the amount "
"accumulated every month you save, \nuntil you reach your goal.";
You can use \n at the end of the first part of the literal for that, like this:
const string PROGRAM_DESCRIPTION = "Program will calculate the amount\n"
"accumulated every month you save, until you reach your goal."; // ^^
You do not have to split the literal into parts if you do not wish to do so for readability:
const string PROGRAM_DESCRIPTION = "Program will calculate the amount accumulated every month you save,\nuntil you reach your goal.";
In C++11, you can use a raw string literal
const char* stuff =
R"foo(this string
is for real)foo";
std::cout << stuff;
outputs:
this string
is for real
(I put this answer here for pedantic reasons, use \n)
Add \n in the string where you want the line to break.
Just insert a line break "\n" in the position you want in your const string, as you would do in a normal literal string:
const string PROGRAM_DESCRIPTION = "Program will calculate the amount\naccumulated every month you save, until you reach your goal.";
cout << PROGRAM_DESCRIPTION;
Simple. Just the same way as you should me used to in using a literal string:
cout << "Program will calculate the amount\naccumulated every month you save, until you reach your goal.";
Right?
Well answers are good, however my offer is to use \r\n for new line. Read here some more, these two symbols should always work (unless you are using Atari 8-bit OS).
And some more explanation.
\n - Line Feed. This should move printing pointer one line lower, but it may or may not set printing pointer to beginning o line.
\r - Carriage Return. This sets line pointer at the beginning o line, and may or may not change line.
\r\n - CR LF. Moves printing pointer to next line and sets it at the beginning of line.

using size_t difference to copy a portion of a string

I am trying to iterate through a string and copy chunks of information based off of an initial key value and a key value that identifies the end of the chunk of info. However when I try to subtract my initial and final values to find the length of the chunk im looking for, I receive a seemingly arbitrary value.
So the start and end indicies are found by:
currentstringlocation = mystring.find("value_im_looking_to_start_at, 0);
endlocation = mystring.find("value_im_looking_to_stop_at", currentstringlocation);
I'm then trying to do something like:
mystring.copy(newstring,(endlocation-currentlocation), currentlocation);
This however isn't giving me the results I want. Here's an excerpt from my code and the output it yields.
stringlocation2=topoinfo.find("\n",stringlocation+11);
topoinfo.copy(address,(stringlocation2-stringlocation+11),stringlocation+11);
cout << (stringlocation2-stringlocation+11) << "\n";
cout << stringlocation2 << "\t" << stringlocation+11 << "\n";
output:
25
59 56
So clearly the chunk of info I'm trying to capture spans 3 characters, however when I subtract the two I get 25. Can someone explain to me why this happens and how I can work around it?
You are calculating the length wrong, try instead something like:
topoinfo.copy(address, stringlocation2 - (stringlocaion + 11),
stringlocation + 11);
After this, address will contain the copied string. Remember though: If address is a character array or a character pointer, then you should add the terminating '\0' character yourself!
A better solution to get a substring is to actually use the std::string::substr function:
std::string address = topoinfo.substr(stringlocation + 11,
stringlocation2 - (stringlocaion + 11));
Should be
topoinfo.copy(address,stringlocation2-(stringlocation+11),stringlocation+11);
cout << stringlocation2-(stringlocation+11) << "\n";
You got your brackets wrong.

Filter strange C++ multimap values

I have this multimap in my code:
multimap<long, Note> noteList;
// notes are added with this method. measureNumber is minimum `1` and doesn't go very high
void Track::addNote(Note &note) {
long key = note.measureNumber * 1000000 + note.startTime;
this->noteList.insert(make_pair(key, note));
}
I'm encountering problems when I try to read the notes from the last measure. In this case the song has only 8 measures and it's measure number 8 that causes problems. If I go up to 16 measures it's measure 16 that causes the problem and so on.
// (when adding notes I use as key the measureNumber * 1000000. This searches for notes within the same measure)
for(noteIT = trackIT->noteList.lower_bound(this->curMsr * 1000000); noteIT->first < (this->curMsr + 1) * 1000000; noteIT++){
if(this->curMsr == 8){
cout << "_______________________________________________________" << endl;
cout << "ID:" << noteIT->first << endl;
noteIT->second.toString();
int blah = 0;
}
// code left out here that processes the notes
}
I have only added one note to the 8th measure and yet this is the result I'm getting in console:
_______________________________________________________
ID:8000001
note toString()
Duration: 8
Start Time: 1
Frequency: 880
_______________________________________________________
ID:1
note toString()
Duration: 112103488
Start Time: 44
Frequency: 0
_______________________________________________________
ID:8000001
note toString()
Duration: 8
Start Time: 1
Frequency: 880
_______________________________________________________
ID:1
note toString()
Duration: 112103488
Start Time: 44
Frequency: 0
This keeps repeating. The first result is a correct note which I've added myself but I have no idea where the note with ID: 1 is coming from.
Any ideas how to avoid this? This loop gets stuck repeating the same two results and I can't get out of it. Even if there are several notes within measure 8 (so that means several values within the multimap that start with 8xxxxxx it only repeats the first note and the non-existand one.
You aren't checking for the end of your loop correctly. Specifically there is no guarantee that noteIT does not equal trackIT->noteList.end(). Try this instead
for (noteIT = trackIT->noteList.lower_bound(this->curMsr * 1000000);
noteIT != trackIT->noteList.end() &&
noteIT->first < (this->curMsr + 1) * 1000000;
++noteIT)
{
For the look of it, it might be better to use some call to upper_bound as the limit of your loop. That would handle the end case automatically.