Description
I have been chasing a problem with time calculations that has now boiled down to the linker is moving non-PROGMEM data into PROGMEM, because it's the same as other data that IS supposed to be in PROGMEM.
I'm running IDE 1.8.10, and the board manager and library manager report everything up to date. OS is Windows7 SP1 fully patched. Reproduces on both an UNO and a Mega 2560.
Before I go further, here's the sketch I've boiled things down to:
#include <Arduino.h>
#include <TimeLib.h>
#include <Timezone.h>
namespace
{
const uint8_t daysArray[] PROGMEM = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
}
// 'TimeChangeRule' parameters to calculate local time.
// N.A. Pacific Time Zone (L.A., Vancouver)
#define DaylightSavingTime "PDT", Second, Sun, Mar, 2, -7 * 60
#define StandardTime "PST", First, Sun, Nov, 2, -8 * 60
TimeChangeRule PDT = {DaylightSavingTime};
TimeChangeRule PST = {StandardTime};
Timezone tz(PDT, PST);
void setup()
{
Serial.begin(115200);
while (!Serial);
tmElements_t tm;
// Wed, 04 Mar 2020 06:52:24 GMT
long tNow = 1583304745L;
Serial.print(F("Current Unix time: "));
Serial.println(tNow);
auto tLocal = tz.toLocal(tNow);
Serial.print(F("Current Local time: "));
Serial.println(tLocal);
// Make sure arrays are linked in by using them
Serial.println((uint32_t)daysArray, HEX);
tm.Year = CalendarYrToTm(2020);
tm.Month = 3; // March
tm.Day = 1;
tm.Hour = 2;
tm.Minute = 0;
tm.Second = 0;
auto t = makeTime(tm);
Serial.println(F("makeTime()"));
Serial.println(t);
}
void loop()
{
}
As is, the sketch botches the time calculation in makeTime(). I traced through the code, and found that in this case, the array of month lengths in the 'Time' library was complete nonsense.
Comment out the following line, and everything works!
Serial.println((uint32_t)daysArray, HEX);
Looking at the map files from this web-site:
https://sparks.gogo.co.nz/avr-ram-use.html
The WORKING version has the following:
RAM: Global Variables/Objects
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
157 Bytes Serial
40 Bytes tz
18 Bytes vtable for HardwareSerial
12 Bytes monthDays
12 Bytes PST
12 Bytes PDT
7 Bytes tm
4 Bytes timer0_overflow_count
4 Bytes timer0_millis
4 Bytes cacheTime
1 Bytes timer0_fract
RAM: Initialisation (eg, Strings not put in Flash)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
800102: 1f 1e adc r1, r31
800104: 1f 1e adc r1, r31
800106: 1f 1f adc r17, r31
800108: 1e 1f adc r17, r30
80010a: 1e 1f adc r17, r30
0080010c <vtable for HardwareSerial>:
80010c: 00 00 00 00 8f 03 ef 02 1c 03 f7 03 4d 03 2b 03 ............M.+.
80011c: 3f 03 0d 0a 00 50 44 54 00 50 53 54 00 00 ?....PDT.PST..
FLASH: Size Of Functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[deleted for brevity]
FLASH: Variable Initial Values and PROGMEM/F() etc...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
52 Bytes setup()
The BROKEN version is:
RAM: Global Variables/Objects
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
157 Bytes Serial
40 Bytes tz
18 Bytes vtable for HardwareSerial
12 Bytes PST
12 Bytes PDT
7 Bytes tm
4 Bytes timer0_overflow_count
4 Bytes timer0_millis
4 Bytes cacheTime
1 Bytes timer0_fract
RAM: Initialisation (eg, Strings not put in Flash)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
800102: 00 00 nop
800104: 95 03 fmuls r17, r21
800106: f5 02 muls r31, r21
800108: 22 03 mulsu r18, r18
80010a: fd 03 fmulsu r23, r21
80010c: 53 03 mulsu r21, r19
80010e: 31 03 mulsu r19, r17
800110: 45 03 mulsu r20, r21
800112: 0d 0a sbc r0, r29
800114: 00 50 subi r16, 0x00 ; 0
800116: 44 54 subi r20, 0x44 ; 68
800118: 00 50 subi r16, 0x00 ; 0
80011a: 53 54 subi r21, 0x43 ; 67
...
FLASH: Size Of Functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[NOTE - removed for brevity]
FLASH: Variable Initial Values and PROGMEM/F() etc...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
52 Bytes setup()
12 Bytes monthDays
12 Bytes (anonymous namespace)::daysArray
Note that the 'monthDays' array has moved to flash! It's a global variable in the 'Time' library, and is NOT marked PROGMEM. If I change any of the values in 'daysArray', then everything works again:
RAM: Global Variables/Objects
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
157 Bytes Serial
40 Bytes tz
18 Bytes vtable for HardwareSerial
12 Bytes monthDays
12 Bytes PST
12 Bytes PDT
7 Bytes tm
4 Bytes timer0_overflow_count
4 Bytes timer0_millis
4 Bytes cacheTime
1 Bytes timer0_fract
RAM: Initialisation (eg, Strings not put in Flash)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
800102: 1f 1e adc r1, r31
800104: 1f 1e adc r1, r31
800106: 1f 1f adc r17, r31
800108: 1e 1f adc r17, r30
80010a: 1e 1f adc r17, r30
0080010c <vtable for HardwareSerial>:
80010c: 00 00 00 00 95 03 f5 02 22 03 fd 03 53 03 31 03 ........"...S.1.
80011c: 45 03 0d 0a 00 50 44 54 00 50 53 54 00 00 E....PDT.PST..
FLASH: Size Of Functions
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[NOTE - removed for brevity]
FLASH: Variable Initial Values and PROGMEM/F() etc...
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
52 Bytes setup()
12 Bytes (anonymous namespace)::daysArray