Arduino, přepínání zímní / letní čas
Moderátor: Moderátoři
Arduino, přepínání zímní / letní čas
Jenže co to přepínání brzo ráno poslední neděli března na letní a poslední neděli října na zimní? Kdyby to byly hodiny, co jdou furt, tak stačí tenhle řádek na detekci přechodu na léto:
if ((dayOfWeek == 7)&&(dayOfMonth >= 25)&&(month == 3)&&(hour == 2)) {pricti = true}
Jenže tady to bude v zařízení, který bude 99% času vypnutý a po zapnutí je potřeba aby zjistilo, že ta přechodová podmínka už byla a provedla se opožděně. Jenže ty údaje o datu a čase už v GPS signálu nejsou. Prostě stejný chování jako když se pustí po změně Wokna a zeptaj se, jestli posunout čas.
Nemáte prosím někdo tip jak na to? Open Ai na tom vybouchl, když jsem testoval kód od něho v reálu, tak to pro každej rok hlásilo jiný kraviny. Možná že byl problém s číslováním prvního dne v týdnu, u nás je 1. den PO. na západě NE.
Zásady jsou celkem jednoduchý, měsíc 11. až 2. je zima a 4. až 9. je léto . Ale ten 3. a 10. nevím jak, přepnutí na letní dochází mezi 25.3. až 31.3 a na zimní mezi 25.10. až 31.10. a datumy se každých 6 let opakují.
Proč mi nemůže všechno chodit hned ?!!
Od zapnutí zařízení do prvního načtení UTC času z GPS by měl být čas v zařízení považován za neznámý a po prvním a každém dalším načtení UTC času z GPS už bude zřejmé zda je to v rámci letního času či nikoli a dle níže zhruba popsané postupu tak vždy z načteného času GPS získáte aktuální lokální čas ČR.
Z tohoto pohledu tedy vůbec nebude nutné mít informaci o tom zda se to už dělalo před posledním vypnutím napájení, protože se to bude dělat vždy po každém načtení času z GPS.
Letní čas SELČ v Česku platí od 1:00 UTC (2:00 SEČ, resp. 3:00 SELČ) poslední neděle v březnu do 1:00 UTC (3:00 SELČ, resp. 2:00 SEČ) poslední neděle v říjnu.
Mělo by stačit použít tuto knihovnu TimeLib a pomocí jejich funkcí po každém načtení UTC času včetně datumu z GPS vypočítat zda je to uvnitř výše zmíněného UTC intervalu pro letní čas či nikoli.
Pokud bude načtený UTC čas z GPS uvnitř UTC intervalu pro letní čas pak víte, že máte ten UTC načtený čas převést na letní čas a pokud to v tom intervalu nebude tak na zimní čas. Pomocí funkcí zmíněné knihovny pak hodnotu UTC času načtenou z GPS posunete příslušným způsobem a s výslednou hodnotou dále pak už budete v zařízení zacházet jako s aktuálním lokálním časem v ČR.
Valdano píše:
Mělo by stačit použít tuto knihovnu TimeLib a pomocí jejich funkcí po každém načtení UTC času včetně datumu z GPS vypočítat zda je to uvnitř výše zmíněného UTC intervalu pro letní čas či nikoli.
Jo, tak právě s touhle knihovnou to OpenAi zkoušel a lezly z toho nesmysly.
Taky mě mate, kde ta knihovna je, ve výpisu knihoven v IDE není a stejně to prochází kompilací.
edit: jo, už jsem ji našel, je schovaná v TimeMaster.
Proč mi nemůže všechno chodit hned ?!!
Kód: Vybrat vše
/*
* TimeGPS.pde
* example code illustrating time synced from a GPS
*
*/
#include <TimeLib.h>
#include <TinyGPS.h> // http://arduiniana.org/libraries/TinyGPS/
#include <SoftwareSerial.h>
// TinyGPS and SoftwareSerial libraries are the work of Mikal Hart
SoftwareSerial SerialGPS = SoftwareSerial(10, 11); // receive on pin 10
TinyGPS gps;
// To use a hardware serial port, which is far more efficient than
// SoftwareSerial, uncomment this line and remove SoftwareSerial
//#define SerialGPS Serial1
// Offset hours from gps time (UTC)
const int offset = 1; // Central European Time
//const int offset = -5; // Eastern Standard Time (USA)
//const int offset = -4; // Eastern Daylight Time (USA)
//const int offset = -8; // Pacific Standard Time (USA)
//const int offset = -7; // Pacific Daylight Time (USA)
// Ideally, it should be possible to learn the time zone
// based on the GPS position data. However, that would
// require a complex library, probably incorporating some
// sort of database using Eric Muller's time zone shape
// maps, at http://efele.net/maps/tz/
time_t prevDisplay = 0; // when the digital clock was displayed
void setup()
{
Serial.begin(9600);
while (!Serial) ; // Needed for Leonardo only
SerialGPS.begin(4800);
Serial.println("Waiting for GPS time ... ");
}
void loop()
{
/* while (SerialGPS.available()) {
if (gps.encode(SerialGPS.read())) { // process gps messages
// when TinyGPS reports new data...
unsigned long age;
int Year;
byte Month, Day, Hour, Minute, Second;
gps.crack_datetime(&Year, &Month, &Day, &Hour, &Minute, &Second, NULL, &age);
if (age < 500) {
// set the Time to the latest GPS reading
setTime(Hour, Minute, Second, Day, Month, Year);
*/
int Year;
byte Month, Day, Hour, Minute, Second;
setTime(0, 0, 0, 1, 3, 25);
adjustTime(offset * SECS_PER_HOUR);
// }
// }
// }
if (timeStatus()!= timeNotSet) {
if (now() != prevDisplay) { //update the display only if the time has changed
prevDisplay = now();
digitalClockDisplay();
}
}
}
void digitalClockDisplay(){
// digital clock display of the time
Serial.print(hour());
printDigits(minute());
printDigits(second());
Serial.print(" ");
Serial.print(day());
Serial.print(" ");
Serial.print(month());
Serial.print(" ");
Serial.print(year());
Serial.println();
}
void printDigits(int digits) {
// utility function for digital clock display: prints preceding colon and leading 0
Serial.print(":");
if(digits < 10)
Serial.print('0');
Serial.print(digits);
}
funkce adjustTime(offset * SECS_PER_HOUR); to nedělá, po celej rok se jen přičítá 1 hodina pro SEČ.
V popisu knihovny ani v jejich příkladech jsem nic k přepínání zimní/letní nenašel.
Proč mi nemůže všechno chodit hned ?!!
Celeron píše:Tak že to v tý knihovně TimeLib Valdano není?
Je tam na to jen částečná podpora a zbytek je potřeba dopsat jak už jsem zmínil. Jen se musí vědět jak správně na to. Níže jsem upravil příklad dle mé specifické varianty řešení. Upravte si to podle toho.
Kód: Vybrat vše
#include <TimeLib.h>
#include <TinyGPS.h>
#include <SoftwareSerial.h>
SoftwareSerial SerialGPS = SoftwareSerial(10, 11); // receive on pin 10
TinyGPS gps;
// do techto promennych je potreba nacitat UTC cas a datum z GPS
// nacitani casu z GPS neni soucast tohoto prikladu
uint8_t gps_sec_utc; // 0 az 59
uint8_t gps_min_utc; // 0 az 59
uint8_t gps_hod_utc; // 0 az 23
uint8_t gps_den_utc; // 1 az 31
uint8_t gps_mes_utc; // 1 az 12
int gps_rok_utc; // napr. 2024
int gps_rok_last = -1; // posledni z GPS nactene cislo roku
unsigned long casovasNacitaniGps;
// pomocne promenne pro zacatek a konec letniho rozmezi casu v UTC
// dle definovanych pravidel ve funkci Nastav_utc_rozsah_letni()
time_t utc_letni_zac;
time_t utc_letni_end;
// do techto promennych se prevadi UTC cas z GPS na lokalni cas CR viz nize
time_t tt_loc_cr;
tmElements_t tm_loc_cr;
void Nastav_utc_rozsah_letni (int rok)
{
int i;
time_t tt;
tmElements_t cs;
// UTC zacatek pro letni casove rozmezi v CR
// od 01:00 posledni nedeli v breznu
cs.Second = 0;
cs.Minute = 0;
cs.Hour = 1;
cs.Day = 1;
cs.Month = 4;
cs.Year = (uint8_t)(rok - 1970);
tt = makeTime(cs) - SECS_PER_DAY;
// najdeme posledni nedeli v breznu
for (i=1; i<8; i++) {
cs.Wday = 0;
breakTime(tt, cs);
if (cs.Wday == 1) {
// nedele
utc_letni_zac = tt;
break;
} else {
tt -= SECS_PER_DAY;
};
};
// UTC konec pro letni casove rozmezi v CR
// do 01:00 posledni nedeli v rijnu
cs.Second = 0;
cs.Minute = 0;
cs.Hour = 1;
cs.Day = 1;
cs.Month = 11;
cs.Year = (uint8_t)(rok - 1970);
tt = makeTime(cs) - SECS_PER_DAY;
// najdeme posledni nedeli v rijnu
for (i=1; i<8; i++) {
cs.Wday = 0;
breakTime(tt, cs);
if (cs.Wday == 1) {
// nedele
utc_letni_end = tt;
break;
} else {
tt -= SECS_PER_DAY;
};
};
}
void Preved_utc_na_lokal()
{
time_t tt;
tmElements_t cs;
cs.Second = gps_sec_utc;
cs.Minute = gps_min_utc;
cs.Hour = gps_hod_utc;
cs.Day = gps_den_utc;
cs.Month = gps_mes_utc;
cs.Year = (uint8_t)(gps_rok_utc - 1970);
tt = makeTime(cs);
if (tt >= utc_letni_zac && tt <= utc_letni_end) {
// UTC cas je v letnim rozmezi pro CR
// takze provedeme prepocet na letni cas UTC + 2h
tt_loc_cr = tt + (SECS_PER_HOUR * 2);
} else {
// UTC cas neni v letnim rozmezi
// takze provedeme prepocet na bezny cas v CR tj. UTC + 1h
tt_loc_cr = tt + SECS_PER_HOUR;
};
breakTime(tt_loc_cr, tm_loc_cr);
}
void digitalClockDisplay()
{
Serial.print(tm_loc_cr.Hour);
printDigits(tm_loc_cr.Minute);
printDigits(tm_loc_cr.Second);
Serial.print(" ");
Serial.print(tm_loc_cr.Day);
Serial.print(" ");
Serial.print(tm_loc_cr.Month);
Serial.print(" ");
Serial.print(1970 + tm_loc_cr.Year);
Serial.println();
}
void printDigits (int digits)
{
Serial.print(":");
if (digits < 10) Serial.print('0');
Serial.print(digits);
}
void setup()
{
utc_leto_zac = 0;
utc_leto_end = 0;
tt_loc_cr = 0;
gps_rok_last = -1;
casovasNacitaniGps = millis();
Serial.begin(9600);
while (!Serial);
SerialGPS.begin(4800);
Serial.println("Cekani na GPS cas... ");
}
void loop()
{
// nenacitat cas z GPS zbytecne casto v kazdem cyklu
// ale treba jen jednou za sekundu
// v programu muzete pouzit promennou gps_rok_last pro detekci nacteni casu
if (gps_rok_last < 0 || (millis() - casovasNacitaniGps) > 1000) {
// od startu programu cas z GPS jeste nebyl nacten nebo uplynula sekunda
if (gps.encode(SerialGPS.read())) {
gps.crack_datetime(&gps_rok_utc, &gps_mes_utc , &gps_den_utc,
&gps_hod_utc, &gps_min_utc, &gps_sec_utc, NULL, NULL);
if (gps_rok_last != gps_rok_utc) {
// nastaveni letniho UTC rozsahu jen pokud se rok lisi od predchoziho volani
gps_rok_last = gps_rok_utc;
Nastav_utc_rozsah_letni(gps_rok_utc);
};
// nyni se zavola prevedeni casu
// napriklad
// UTC cas 17:50:00 28.4.2024 prevede na lokalni letni cas 19:50:00 28.4.2024
Preved_utc_na_lokal();
// a nyni bude v
// tt_loc_cr lokalni cas CR jako cislo (pocet sekund o zacatku roku 1970)
// tm_loc_cr lokalni cas CR jako struktura tmElements_t
// POZOR: tm_loc_cr.Year je hodnota 0 az 255 kde 0=1970
// takze souctem 1970 + tm_loc_cr.Year ziskate spravnou hodnotu roku
digitalClockDisplay();
};
casovasNacitaniGps = millis();
};
}Opravil jsem dvě chyby v syntaxi, co vyhodil kompiler IDE. Zrušil jsem načítání z GPS a v definicích na začátku jsem ručně doplňoval datum a čas. Zkoušel jsem zatím jen rok 2028 a vypadá to, že maká jak má. Tedy nejsem si jistej, jestli se správně přepíná podle hodin, posuď sám:
23.3. 2028 pokud je UTC 0:0:0, tak výpis času je 1:0:0. Pokud je UTC 1:0:0 tak je výpis 3:0:0.
29.10.2028 pokud je UTC 1:0:0, tak výpis času je 3:0:0. Pokud je UTC 2:0:0 tak je výpis 3:0:0.
Problém je, že to zabírá v 328P pětinu flešky a čtvrtinu RAMky a když k tomu přidám hlavní část projektu, pro kterou je tenhle doplněk, tak se to nevejde, musel bych použít Every.
Zkouším tady něco svýho přes tabulku, kde je 6 začátků a 6 konců letního času ve formátu DD:MM:RRRR pro rok 2024 až 2029. Když se to stále opakuje po šesti letech, tak stačí jen od roku většího než 2029 odečístat 6 aby se rok dostal do rozsahu 2024 až 2029. Hodiny neřeším, jen dny. Rozsahy měsíc 11.-2. a 4.-9. jsou jasný hned, takže zbývá jen 3. a 10. měsíc. Ten 3. měsíc mi chodí jak má, ale 10. ne a nevím proč. Je to zatím asi 4 x menší na obou pamětech, než co jsi napsal Ty. Když se mi to nepovede zprovoznit, tak budu muset sáhnout na to Tvý řešení. Možná bys to měl dát na Github, aby z toho měli užitek i ostatní.
Ještě jednou díky!
Proč mi nemůže všechno chodit hned ?!!
Celeron píše:Problém je, že to zabírá v 328P pětinu flešky a čtvrtinu RAMky a když k tomu přidám hlavní část projektu, pro kterou je tenhle doplněk, tak se to nevejde, musel bych použít Every.
A co použít třeba WeMos D1 Mini? Tam je 4MB Flash a 64 KB SRAM což je mnohem víc místa než v 328P i Every. Nemluvě o mnohem výkonnějším procesoru.
Kód: Vybrat vše
#include <TimeLib.h> // Knihovna pro práci s časem
#include <avr/pgmspace.h>
const int letniCasOdDo[][4] PROGMEM = {
{31, 3, 2024}, // Datum začátku letního času v roce 2024
{27, 10, 2024}, // Datum konce letního času v roce 2024
{30, 3, 2025}, // Datum začátku letního času v roce 2025
{26, 10, 2025}, // Datum konce letního času v roce 2025
{29, 3, 2026}, // Datum začátku letního času v roce 2026
{25, 10, 2026}, // Datum konce letního času v roce 2026
{28, 3, 2027}, // Datum začátku letního času v roce 2027
{31, 10, 2027}, // Datum konce letního času v roce 2027
{26, 3, 2028}, // Datum začátku letního času v roce 2028
{29, 10, 2028}, // Datum konce letního času v roce 2028
{25, 3, 2029}, // Datum začátku letního času v roce 2029
{28, 10, 2029}, // Datum konce letního času v roce 2029
};
const int pocetZmenCasu = sizeof(letniCasOdDo) / sizeof(letniCasOdDo[0]);
//***********************************************
void setup() {
Serial.begin(9600);
// Nastavení aktuálního času
setTime(0, 0, 0, 26, 3, 2028); // Nastavte počáteční datum
// Kontrola, zda je aktuální čas v letním čase nebo ne
bool letniCas = jeLetniCas(year(), month(), day(), hour());
if (letniCas) {
Serial.println("Aktuálně je letní čas.");
} else {
Serial.println("Aktuálně je zimní čas.");
}
}
//************************************************
bool jeLetniCas(int year, int month, int day, int hour) {
// Odečíst 6 let, dokud není rok nižší nebo roven 2029
while (year > 2029) {
year -= 6;
}
// Pokud je měsíc mezi dubnem a zářím, je jednoznačně letní čas
if (month > 3 && month < 10) {
return true;
}
// Pokud je měsíc mezi listopadem a únorem, je jednoznačně zimní čas
if (month < 3 || month > 10) {
return false;
}
// Pokud je měsíc březen nebo říjen, zkontrolujeme, zda je datum změny na letní čas
for (int i = 0; i < pocetZmenCasu; i++) {
if (month == 3) {
if (pgm_read_byte(&(letniCasOdDo[i][0])) <= day && pgm_read_byte(&(letniCasOdDo[i][1])) == month && pgm_read_word(&(letniCasOdDo[i][2])) == year) {
return true; // začíná letní čas
}
}
if (month == 10) {
if (pgm_read_byte(&(letniCasOdDo[i+1][0])) > day && pgm_read_byte(&(letniCasOdDo[i+1][1])) == month && pgm_read_word(&(letniCasOdDo[i+1][2])) == year) {
return true; //ještě je letní čas
}
}
}
return false;
}
//***************************************
void loop() {
}
//***************************************
Když byla tabulka změn v RAM, tak 9% flash a 18% RAM. Po použití PROGMEM tedy přesunu tabulky do flash je kupodivu stále 9%flash ale jen 13% RAM.
Program je zjednodušený, dny kdy se mění čas jsou změněný už od půlnoci. Pro moji potřebu to vůbec nevadí.
Tak teď ten odzkoušený příklad zapracovat do zobrazení času v GPS.
Proč mi nemůže všechno chodit hned ?!!
Celeron píše:Nelze, potřebuju víc analogů.
V tom případě zvažte ESP32-DevKitC. Tam je víc analogů i binárů a jsou tam myslím dokonce 2 mega RAM.
Celeron píše:Když byla tabulka změn v RAM, tak 9% flash a 18% RAM. Po použití PROGMEM tedy přesunu tabulky do flash je kupodivu stále 9%flash ale jen 13% RAM.
ATmega328P má 1024 bajtů EEPROM. Můžete tedy zvážit uložení té tabulky do EEPROM, ale i tak si moc nepomůžete, protože jste se zbytkem RAM a FLASH už docela nízko. Takže v případě nějakého rozšiřování v budoucnu zřejmě znovu narazíte. ESP32 by tedy bylo řešením na hodně dlouho do budoucna bez nutnosti se obávat, že vám dojde FLASH či RAM.
Celeron píše:Problém je, že to zabírá v 328P pětinu flešky a čtvrtinu RAMky a když k tomu přidám hlavní část projektu, pro kterou je tenhle doplněk, tak se to nevejde, musel bych použít Every.
Navíc i když jste to teď tou tabulkou obešel tak to pořád není žádná sláva, protože 328P má celkově jenom 2KB RAM, a to opravdu není nic moc. Takže do budoucna stačí zas nějaká další trochu na paměť náročnější úprava a zas budete bojovat s pamětí. Použití ESP32 by bylo řešením na dlouho do budoucna bez jakýchkoli obav o nedostatek místa v RAM nebo FLASH a analogových i binárních IO tam rozhodně je dostatek.