HAL_SetDate sets the year to wrong value - stm32

I'm using STM32F030RCT6 with CubeMX. Device is a datalogger and RTC is the main thing that cannot fail. On Errata Sheet there is something about RTC Shadow Register.
I configured CubeMX to not generate MX_RTC_Init() function and it has been working normally so far. (I'm using LSE)
I need to update the time/date from GSM time but when I set the year to 18 with HAL_SetDate() and after a small delay I read with HAL_GetDate(), sDate.Year gave me 20. Apart from Year, the other values are correct.
What I tried:
Used LSI
while (HAL_SetDate != HAL_GetDate) HAL_SetDate(ActualDate)
First HAL_GetDate then HAL_SetDate
I got no progress and thing even got worse like Month = 56, Day = 45 etc.
Thanks in advance.
Best regards.

The value WeekDay must be set to a value between 0 to 7
I had the same problem. I found that the problem was: Not setting a value to WeekDay. When creating a struct RTC_DateTypeDef in a functions scope, the field WeekDay gets a random value. I discovered that: The value WeekDay must be set to a value between 0 to 7, if it is out of this range, it can change the year.
Explanation:
The code for setting the date in function HAL_RTC_SetDate:
if (Format == RTC_FORMAT_BIN)
{
assert_param(IS_RTC_YEAR(sDate->Year));
assert_param(IS_RTC_MONTH(sDate->Month));
assert_param(IS_RTC_DATE(sDate->Date));
datetmpreg = (((uint32_t)RTC_ByteToBcd2(sDate->Year) << 16U) | \
((uint32_t)RTC_ByteToBcd2(sDate->Month) << 8U) | \
((uint32_t)RTC_ByteToBcd2(sDate->Date)) | \
((uint32_t)sDate->WeekDay << 13U));
}
else
{
assert_param(IS_RTC_YEAR(RTC_Bcd2ToByte(sDate->Year)));
assert_param(IS_RTC_MONTH(RTC_Bcd2ToByte(sDate->Month)));
assert_param(IS_RTC_DATE(RTC_Bcd2ToByte(sDate->Date)));
datetmpreg = ((((uint32_t)sDate->Year) << 16U) | \
(((uint32_t)sDate->Month) << 8U) | \
((uint32_t)sDate->Date) | \
(((uint32_t)sDate->WeekDay) << 13U));
}
Date occupies bits 0-7 (8 bits): two BCD digits.
Month occupies bits 8-12 (5 bits): two BCD digits but the left digit can
only be 0 or 1 -> 5 bits is enough.
WeekDay occupies bits 13-15 (3 bits): one BCD digit with value range 1-7 -> 3 bits
is enough.
Year occupies bits 16-24 (9 bits).
When WeekDay is greater than 7, the MSB is 1 and it overlaps with the LSB of Year and can change it (if the LSB is 0).

I had exactly the same issue with setting the year value after using HAL_SetDate(). Wrong values was read with the Hal_GetDate() function.
MX_RTC_Init() checks first a particular value in a RTC BKP register to know if a Reset occurred on the RTC domain. It prevents from setting again the time and date if it has been already done.
I finally did the same and tadaaaam for the first time I'm reading a good year value of 18 !!
if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR10) != 0x32F2){ // Mandatory: workaround found ?
if(HAL_RTC_SetDate(&hrtc, &sDate, RTC_FORMAT_BIN) == HAL_OK){
if(HAL_RTC_SetTime(&hrtc, &sTime, RTC_FORMAT_BIN) == HAL_OK){
HAL_RTCEx_BKUPWrite(&hrtc,RTC_BKP_DR10, 0x32F2);
}
}
}
I do not understand precisely why it works, I'll ask on ST forum and edit this answer as soon as I know.
In the meantime could you check if it works for you too ?

Set your WeekDay parameter to a valid value.

Related

Heart Rate Value in BLE

I am having a hard time getting a valid value out of the HR characteristics. I am clearly not handling the values properly in Dart.
Example Data:
List<int> value = [22, 56, 55, 4, 7, 3];
Flags Field:
I convert the first item in the main byte array to binary to get the flags
22 = 10110 (as binary)
this leads me to believe that it is U16 (bit[0] is == 1)
HR Value:
Because it is 16 bit I am trying to get the bytes in the 1 & 2 indexes. I then try to buffer them into a ByteData. From there I get convert them to Uint16 with the Endian set to Little. This is giving me a value of 14136. Clearly I am missing something fundamental about how this is supposed to work.
Any help in clearing up what I am not understanding about how to process the 16 bit BLE values would be much appreciated.
Thank you.
/*
Constructor - constructs the heart rate value from a BLE message
*/
HeartRate(List<int> values) {
var flags = values[0];
var s = flags.toRadixString(2);
List<String> flagsArray = s.split("");
int offset = 0;
//Determine whether it is U16 or not
if (flagsArray[0] == "0") {
//Since it is Uint8 i will only get the first value
var hr = values[1];
print(hr);
} else {
//Since UTF 16 is two bytes I need to combine them
//Create a buffer with the first two bytes after the flags
var buffer = new Uint8List.fromList(values.sublist(1, 3)).buffer;
var hrBuffer = new ByteData.view(buffer);
var hr = hrBuffer.getUint16(0, Endian.little);
print(hr);
}
}
Your updated data looks much better. Here's how to decode it, and the process you'd use to figure this out yourself from scratch.
Determine the format
The Bluetooth site has been reorganized recently (~2020), and in particular they got rid of some of the document viewers, which makes things much harder to find and read IMO. All the documentation is in the Heart Rate Service (HRS) document, linked from the main GATT page, but for just parsing the format, the best source I know of is the XML for org.bluetooth.characteristic.heart_rate_measurement. (Since the reorganization, I don't know how you can find this page without searching for it. It doesn't seem to be linked anymore.)
Byte 0 - Flags: 22 (0001 0110)
Bits are numbered from LSB (0) to MSB (7).
Bit 0 - Heart Rate Value Format: 0 => UINT8 beats per minute
Bit 1-2 - Sensor Contact Status: 11 => Supported and detected
Bit 3 - Energy Expended Status: 0 => Not present
Bit 4 - RR-Interval: 1 => One or more values are present
The meaning of RR-intervals is explained in the HRS document, linked above. It sounds like you just want the heart rate value, so I won't go into them here.
Byte 1 - UINT8 BPM: 56
Since Bit 0 of flags was 0, this is the beats per minute. 56.
Bytes 2-5 - UINT16 RR Intervals: 55, 4, 7, 3
You probably don't care about these, but there are two UINT16 values here (there can be an arbitrary number of RR-Interval values). BLE is always little-endian, so [55, 4] is 1,079 (55 + 4<<8), and [7, 3] is 775 (7 + 3<<8).
I believe the docs are a little confusing on this one. The XML suggests that these values are in seconds, but the comments say "Resolution of 1/1024 second." The normal way to express this would be <BinaryExponent>-10</BinaryExponent>, and I'm certain that's what they meant. So these would be:
RR0: 1.05s (1079/1024)
RR1: 0.76s (775/1024)

Strange Date object in MongoDB [duplicate]

As per MDN
"Date objects are based on a time value that is the number of milliseconds since 1 January, 1970 UTC."
Then why does it accept negative values ?
Even if it did shouldn't negative value mean values before Jan 1, 1970 ?
new Date('0000', '00', '-1'); // "1899-12-30T05:00:00.000Z"
new Date('0000', '00', '00'); // "1899-12-31T05:00:00.000Z"
new Date('-9999', '99', '99'); // "-009991-07-08T04:00:00.000Z"
What is happening ?
Update
For some positive values , the year begins from 1900
new Date(100); // "1970-01-01T00:00:00.100Z" // it says 100Z
new Date(0100); // "1970-01-01T00:00:00.064Z" // it says 64Z
new Date("0006","06","06"); // "1906-07-06T04:00:00.000Z"
Also note that, in the last one, the date is shown as 4 which is wrong.
I suspect this is some sort of Y2K bug ?!!
This is hard and inconsistent, yes. The JavaScript Date object was based on the one in Java 1.0, which is so bad that Java redesigned a whole new package.
JavaScript is not so lucky.
Date is "based on" unix epoch because of how it is defined. It's internal details.
1st Jan 1970 is the actual time of this baseline.
since is the direction of the timestamp value: forward for +ve, backward for -ve.
Externally, the Date constructor has several different usages, based on parameters:
Zero parameters = current time
new Date() // Current datetime. Every tutorial should teach this.
The time is absolute, but 'displayed' timezone may be UTC or local.
For simplicity, this answer will use only UTC. Keep timezone in mind when you test.
One numeric parameter = timestamp # 1970
new Date(0) // 0ms from 1970-01-01T00:00:00Z.
new Date(100) // 100ms from 1970 baseline.
new Date(-10) // -10ms from 1970 baseline.
One string parameter = iso date string
new Date('000') // Short years are invalid, need at least four digits.
new Date('0000') // 0000-01-01. Valid because there are four digits.
new Date('1900') // 1900-01-01.
new Date('1900-01-01') // Same as above.
new Date('1900-01-01T00:00:00') // Same as above.
new Date('-000001') // 2 BC, see below. Yes you need all those zeros.
Two or more parameters = year, month, and so on # 1900 or 0000
new Date(0,0) // 1900-01-01T00:00:00Z.
new Date(0,0,1) // Same as above. Date is 1 based.
new Date(0,0,0) // 1 day before 1900 = 1899-12-31.
new Date(0,-1) // 1 month before 1900 = 1899-12-01.
new Date(0,-1,0) // 1 month and 1 day before 1900 = 1899-11-30.
new Date(0,-1,-1) // 1 month and *2* days before 1900 = 1899-11-29.
new Date('0','1') // 1900-02-01. Two+ params always cast to year and month.
new Date(100,0) // 0100-01-01. Year > 99 use year 0 not 1900.
new Date(1900,0) // 1900-01-01. Same as new Date(0,0). So intuitive!
Negative year = BC
new Date(-1,0) // 1 year before 0000-01-01 = 1 year before 1 BC = 2 BC.
new Date(-1,0,-1) // 2 days before 2 BC. Fun, yes? I'll leave this as an exercise.
There is no 0 AC. There is 1 AC and the year before it is 1 BC. Year 0 is 1 BC by convention.
2 BC is displayed as year "-000001".
The extra zeros are required because it is outside normal range (0000 to 9999).
If you new Date(12345,0) you will get "+012345-01-01", too.
Of course, the Gregorian calendar, adopted as late as 1923 in Europe, will cease to be meaningful long before we reach BC.
In fact, scholars accept that Jesus wasn't born in 1 BC.
But with the stars and the land moving at this scale, calendar is the least of your worries.
The remaining given code are just variations of these cases. For example:
new Date(0100) // One number = epoch. 0100 (octal) = 64ms since 1970
new Date('0100') // One string = iso = 0100-01-01.
new Date(-9999, 99, 99) // 9999 years before BC 1 and then add 99 months and 98 days
Hope you had some fun time. Please don't forget to vote up. :)
To stay sane, keep all dates in ISO 8601 and use the string constructor.
And if you need to handle timezone, keep all datetimes in UTC.
Well, firstly, you're passing in string instead of an integer, so that might have something to do with your issues here.
Check this out, it explains negative dates quite nicely, and there is an explanation for your exact example.
Then why does it accept negative values ?
You are confusing the description of how the data is stored internally with the arguments that the constructor function takes.
Even if it did shouldn't negative value mean values before Jan 1, 1970 ?
No, for the above reason. Nothing stops the year, month or day from being negative. You just end up adding a negative number to something.
Also note that, in the last one, the date is shown as 4 which is wrong.
Numbers which start with a 0 are expressed in octal, not decimal. 0100 === 64.
Please have a look at the documentation
Year: Values from 0 to 99 map to the years 1900 to 1999
1970 with appropriate timezone: new Date(0); // int MS since 1970
1900 (or 1899 with applied timezone): new Date(0,0) or new Date(0,0,1) - date is 1 based, month and year are 0 based
1899: new Date(0,0,-1)

Convert unix time to month number?

Using os.time how can I get how many months have passed since the unix epoch (Unix Timestamp)
I just need it for a month ID, so any kind of number would be fine, as long as it changes every month, and it can be reversed to get the actual month.
local function GetMonth(seconds)
local dayduration,year = 3600*24
local days={31,0,31,30,31,30,31,31,30,31,30,31}
for i=1970,10000 do -- For some reason too lazy to use while
local yeardays = i%4 == 0 and i%100 ~= 0 and 366 or 365
local yearduration = dayduration * yeardays
if yearduration < seconds then
seconds = seconds - yearduration
else
year = i break
end
end
days[2]=(year%4==0) and 29 or 28
seconds = seconds%(365*24*3600)
for i=1,12 do
if seconds>days[i]*dayduration then
seconds=seconds-days[i]*dayduration
else
return --i + year*12 <-- If you want a unique ID
end
end
end
Currently, it'll give the number 2, since it's February. If you uncomment the code at the end for the unique ID, you'll get 554 instead, meaning we're currently at the 554th month since the epoch.
As Jean-Baptiste Yunès said in his answer's comments, I'm not sure if your sentence:
NOTE: This is for Lua, but I'm unable to use os.date
meant you have no os.date, or that you don't know how to use it. You have an answer for both cases, you can use the one you need.
This may do the trick:
print (os.date("*t",os.time())["month"])
os.time() gives you the current date as a number. os.date("*t",...) converts it into a table in which the month equals to the number of the month corresponding to the date.

Applescript: return specific index positions from a date string

I have already used the text delimiters and item numbers to extract a date from a file name, so I'm clear about how to use these. Unfortunately the date on these particular files are formatted as "yyyyMMdd" and I need to covert the date into format "yyyy-MM-dd". I have been trying to use the offset function to get particular index positions, and I have found several examples of how you would return the offset of particular digits in the string, example:
set theposition to offset of 10 in theString -- this works
(which could return 5 or 7) but I have not found examples of how to call the digits at a specific index:
set _day to offset 7 of file_date_raw -- error
"Finder got an error: Some parameter is missing for offset." number -1701
How would you do this, or is there a totally better way I'm unaware of?
To "call the digits at a specific index", you use:
text 1 thru 4 of myString
If you know that each string has 8 characters in the yyyymmdd format, then you don't need to use 'offset' or any parsing, just add in the -'s, using text x thru y to dissect the string.
set d to "20011018"
set newString to (text 1 thru 4 of d) & "-" & (text 5 thru 6 of d) & "-" & (text 7 thru 8 of d)

Unix gettimeofday() - compatible algorithm for determining week within month?

If I've got a time_t value from gettimeofday() or compatible in a Unix environment (e.g., Linux, BSD), is there a compact algorithm available that would be able to tell me the corresponding week number within the month?
Ideally the return value would work in similar to the way %W behaves in strftime() , except giving the week within the month rather than the week within the year.
I think Java has a W formatting token that does something more or less like what I'm asking.
[Everything below written after answers were posted by David Nehme, Branan, and Sparr.]
I realized that to return this result in a similar way to %W, we want to count the number of Mondays that have occurred in the month so far. If that number is zero, then 0 should be returned.
Thanks to David Nehme and Branan in particular for their solutions which started things on the right track. The bit of code returning [using Branan's variable names] ((ts->mday - 1) / 7) tells the number of complete weeks that have occurred before the current day.
However, if we're counting the number of Mondays that have occurred so far, then we want to count the number of integral weeks, including today, then consider if the fractional week left over also contains any Mondays.
To figure out whether the fractional week left after taking out the whole weeks contains a Monday, we need to consider ts->mday % 7 and compare it to the day of the week, ts->wday. This is easy to see if you write out the combinations, but if we insure the day is not Sunday (wday > 0), then anytime ts->wday <= (ts->mday % 7) we need to increment the count of Mondays by 1. This comes from considering the number of days since the start of the month, and whether, based on the current day of the week within the the first fractional week, the fractional week contains a Monday.
So I would rewrite Branan's return statement as follows:
return (ts->tm_mday / 7) + ((ts->tm_wday > 0) && (ts->tm_wday <= (ts->tm_mday % 7)));
If you define the first week to be days 1-7 of the month, the second week days 8-14, ... then the following code will work.
int week_of_month( const time_t *my_time)
{
struct tm *timeinfo;
timeinfo =localtime(my_time);
return 1 + (timeinfo->tm_mday-1) / 7;
}
Assuming your first week is week 1:
int getWeekOfMonth()
{
time_t my_time;
struct tm *ts;
my_time = time(NULL);
ts = localtime(&my_time);
return ((ts->tm_mday -1) / 7) + 1;
}
For 0-index, drop the +1 in the return statement.
Consider this pseudo-code, since I am writing it in mostly C syntax but pretending I can borrow functionality from other languages (string->int assignment, string->time conversion). Adapt or expand for your language of choice.
int week_num_in_month(time_t timestamp) {
int first_weekday_of_month, day_of_month;
day_of_month = strftime(timestamp,"%d");
first_weekday_of_month = strftime(timefstr(strftime(timestamp,"%d/%m/01")),"%w");
return (day_of_month + first_weekday_of_month - 1 ) / 7 + 1;
}
Obviously I am assuming that you want to handle weeks of the month the way the standard time functions handle weeks of the year, as opposed to just days 1-7, 8-13, etc.