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.
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 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NOTE:
Inlined functions are not shown separately, they are added to the function using them.
The compiler may choose to inline functions not declared inline for performance.
If a function is unusually large, look to see if any of the functions used by it
have been inlined (by seeing they are not listed separately).
636 Bytes main
266 Bytes Timezone::toTime_t(TimeChangeRule, int) [clone:.constprop.10]
232 Bytes Timezone::calcTimeChanges(int) [clone:.constprop.9]
---------------------------------------------------------------------------
498 Bytes Timezone TOTAL
438 Bytes breakTime(unsigned:long, tmElements_t&) [clone:.constprop.11]
154 Bytes HardwareSerial::write(unsigned:char)
68 Bytes HardwareSerial::_tx_udr_empty_irq()
64 Bytes HardwareSerial::flush()
40 Bytes HardwareSerial::read()
30 Bytes HardwareSerial::availableForWrite()
28 Bytes HardwareSerial::peek()
24 Bytes HardwareSerial::available()
---------------------------------------------------------------------------
408 Bytes HardwareSerial TOTAL
324 Bytes makeTime(tmElements_t const&) [clone:.constprop.12]
76 Bytes __vector_19
100 Bytes __vector_18
148 Bytes __vector_16
---------------------------------------------------------------------------
324 Bytes Interrupt Handling (Vectors) TOTAL
318 Bytes global constructors keyed:to 65535_0_DST.cpp.o.1937
156 Bytes Print::printNumber(unsigned:long, unsigned:char) [clone:.constprop.18]
90 Bytes Print::write(unsigned:char const*, unsigned:int)
54 Bytes Print::print(__FlashStringHelper const*) [clone:.constprop.14]
16 Bytes Print::println() [clone:.constprop.20]
---------------------------------------------------------------------------
316 Bytes Print TOTAL
78 Bytes year(unsigned:long)
30 Bytes strncpy
20 Bytes serialEventRun()
20 Bytes Serial0_available()
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! 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
So, armed with this, what would be my next step?