Retrográdní hodiny
Ing. Jaroslav Kavalír nám poslal popis velice zajímavého projektu – “Retrográdní hodiny” – tedy hodin, které k vizualizaci času používají analogové voltmetry. Ty byly vyrobeny někdy kolem roku 1950 a pocházejí ze sovětského servisního přístroje.
Obsah článku:
Voltmetry
Voltmetry ukazují popořadě dny v týdnu, hodiny, minuty, vteřiny a teplotu. Čas se dá nastavit přes USB z PC. Pro běžnou korekci je určeno tlačítko, které vynuluje sekundy. Letní čas je nastavován automaticky softwarově. Poslední voltmeter ukazuje teplotu.
RTC DS3231
Aktuální čas zajišťuje modul reálného DS3231.
Srdcem hodin je modul hodin reálného času DS3231. Výrobce uvádí přesnost ±2ppm od 0°C do +40°C což je asi 5 vteřin za měsíc. Čas z modulu je čten mikroprocesorem Arduino Uno přes sběrnici I2C. Časové údaje jsou převedeny pomocí PWM regulátorů na napětí a to je přes odpory a trimry přivedeno na voltmetry. Trimry je možné při oživování nastavit přesný rozsah měřidla. To by se dalo udělat i softwarově, ale takhle je to jednodušší. Velikost předřadného odporu se vypočte podle použitého voltmetru. V mém případě byl potřeba na plnou výchylku proud 1 mA. Takže při 5 V je to 5 kOhm. Použil jsem odpory 4k7 v sérii s 1k trimrem. Běžně bývá u voltmetru na plnou výchylku 100 uA, takže hodnoty budou 10x vyšší.
Program
Vlastní program je složen z toho co jsem posbíral na WEBu bez toho, že bych kód nějak optimalizoval.
Ke stažení naleznete program zde.
/* Zobrazuje čas a teplotu na 5-ti voltmetrech. * DS3231 je propojen na Arduino-Uno; SDA-A4; SLC-A5; +5V; GND * Korekce pomocí tlačítka. Pro 0 - 30 se sekundy vynulují a pro 30 - 59 se nastaví na 59. Je nastavován i letní čas. Ispirace Arduino.cz a WEB */ #include "Wire.h" #define DS3231_I2C_ADDRESS 0x68 // Convert normal decimal numbers to binary coded decimal byte decToBcd(byte val){ return( (val/10*16) + (val%10) ); } // Convert binary coded decimal to normal decimal numbers byte bcdToDec(byte val){ return( (val/16*10) + (val%16) ); } void setup(){ Wire.begin(); pinMode(3, OUTPUT); pinMode(5, OUTPUT); pinMode(6, OUTPUT); pinMode(10, OUTPUT); pinMode(11, OUTPUT); pinMode(8, INPUT); Serial.begin(9600); // set the initial time here: // DS3231 seconds, minutes, hours, day, date, month, year // setDS3231time(50,27,11,04,1,12,0); //****tady se nastavi poprve hodiny a pak REM //*** // } void setDS3231time(byte second, byte minute, byte hour, byte dayOfWeek, byte dayOfMonth, byte month, byte year){ // sets time and date data to DS3231 Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0); // set next input to start at the seconds register Wire.write(decToBcd(second)); // set seconds Wire.write(decToBcd(minute)); // set minutes Wire.write(decToBcd(hour)); // set hours Wire.write(decToBcd(dayOfWeek)); // set day of week (1=Sunday, 7=Saturday) Wire.write(decToBcd(dayOfMonth)); // set date (1 to 31) Wire.write(decToBcd(month)); // set month Wire.write(decToBcd(year)); // set year (0 to 99) Wire.endTransmission(); } void readDS3231time(byte *second, byte *minute, byte *hour, byte *dayOfWeek, byte *dayOfMonth, byte *month, byte *year){ Wire.beginTransmission(DS3231_I2C_ADDRESS); Wire.write(0); // set DS3231 register pointer to 00h Wire.endTransmission(); Wire.requestFrom(DS3231_I2C_ADDRESS, 7); // request seven bytes of data from DS3231 starting from register 00h *second = bcdToDec(Wire.read() & 0x7f); *minute = bcdToDec(Wire.read()); *hour = bcdToDec(Wire.read() & 0x3f); *dayOfWeek = bcdToDec(Wire.read()); *dayOfMonth = bcdToDec(Wire.read()); *month = bcdToDec(Wire.read()); *year = bcdToDec(Wire.read()); } void displayTime(){ pinMode(8,INPUT_PULLUP); int tlac = 8; byte second, minute, hour, dayOfWeek, dayOfMonth, month, year; // retrieve data from DS3231 readDS3231time(&second, &minute, &hour, &dayOfWeek, &dayOfMonth, &month, &year); tlac = !digitalRead(8); //čtení tlačítka korekce a negace byte se = second; //převod na pomocné proměnné byte mi = minute; byte ho = hour; byte we = dayOfWeek; byte dm = dayOfMonth; byte mo = month; byte ye = year; //korekce sekund zpět if (tlac>0 && se<30) { setDS3231time(0,mi,ho,we,dm,mo,ye); } //korekce sekund vpřed if (tlac>0 && se>30) { setDS3231time(59,mi,ho,we,dm,mo,ye); } //změna času na letní - poslední neděle v březnu ve 2 hodiny if ((dayOfWeek == 7)&&(dayOfMonth >= 25)&&(month == 3)&&(hour == 2)) { setDS3231time(se,mi,3,we,dm,mo,ye); } // nastavení hodin na 3 hodinu //změna času na zimní if ((dayOfWeek == 7)&&(dayOfMonth >= 25)&&(month == 10)&&(hour == 1)&&(year != 1)) { setDS3231time(se,mi,ho,we,dm,mo,1); } //rok použit jako indikace, že bylo léto if ((dayOfWeek == 7)&&(dayOfMonth >= 25)&&(month == 10)&&(hour == 3)&&(year == 1)) { setDS3231time(se,mi,2,we,dm,mo,0); } // nastavení hodin na 2 hodinu a příznak na 0 // příznak je tam, aby se ve 3 hodiny znovu nenastavovalo na 2 //------------------------------------------------------------------ // send it to the serial monitor Serial.print(hour, DEC); // convert the byte variable to a decimal number when displayed Serial.print(":"); if (minute<10){ Serial.print("0"); } Serial.print(minute, DEC); Serial.print(":"); if (second<10){ Serial.print("0"); } Serial.print(second, DEC); // send it to the Voltmetrs ---------------------------------------- second = second * 4.25; // 60 sec na 255 dílků analogWrite(3,second); // nastaveni plneni PWM minute = (minute * 4.25) + (second/60); // 60 min na 255 dílků plus vteřiny analogWrite(6,minute); // nastaveni plneni PWM if (hour > 12 ){hour = hour - 12; } else { hour = hour; } hour = hour + 6; //posunuti stupnice, 12 uprostred hour = (hour * 21.25) + (minute/12); //12 hodin na 255 dílků plus minuty analogWrite(10,hour); // nastaveni plneni PWM dayOfWeek = (dayOfWeek * 36.4) - 16; analogWrite(11,dayOfWeek); //-------------------------------------------- Serial.print(" "); Serial.print(dayOfMonth, DEC); Serial.print("/"); Serial.print(month, DEC); Serial.print("/"); Serial.print(year, DEC); Serial.print(" "); Serial.print(we, DEC); Serial.print(" den "); Serial.print(" "); switch(we){ case 1: Serial.print("pondeli "); break; case 2: Serial.print("utery "); break; case 3: Serial.print("streda "); break; case 4: Serial.print("ctvrtek "); break; case 5: Serial.print("patek "); break; case 6: Serial.print("sobota "); break; case 7: Serial.print("nedele "); break; } } void loop(){ displayTime(); // display the real-time clock data on the Serial Monitor, delay(200); // every 0,2 second #define DS3231_I2C_ADDR 0x68 #define DS3231_TEMPERATURE_ADDR 0x11 int tempC = DS3231_get_treg(); // Reads the temperature as an int, to save memory // float tempC = DS3231_get_treg(); byte teplota; Serial.print(tempC); Serial.println("C "); teplota = (tempC-11.5)*6.125 ; //stupnice nastavena na rozsah5 až 250 //korigováno na zvýšenou teplotu IO analogWrite(5, teplota); //teplota je v PWM } float DS3231_get_treg() { int rv; // Reads the temperature as an int, to save memory uint8_t temp_msb, temp_lsb; int8_t nint; Wire.beginTransmission(DS3231_I2C_ADDR); Wire.write(DS3231_TEMPERATURE_ADDR); Wire.endTransmission(); Wire.requestFrom(DS3231_I2C_ADDR, 2); temp_msb = Wire.read(); temp_lsb = Wire.read() >> 6; if ((temp_msb & 0x80) != 0) nint = temp_msb | ~((1 << 8) - 1); // if negative get two's complement else nint = temp_msb; rv = 0.25 * temp_lsb + nint; return rv; }
Význam kódu
Řádek | Popis |
---|---|
11 | Tady se při nastavování nastaví zda je právě letní nebo zimní čas |
30 | Tady se při nastavování zapíše aktuální čas, program se nahraje do Arduina a pak se tento řádek zaREMuje. |
33-60 | Komunikace s modulem DS3231, převzato z WEBu tak jak je |
70 | Hodiny mají jediné korekční tlačítko na Pin 8 |
80-87 | Při stisknutí korekčního tlačítka v intervalu 0 < 30 sekund se sekundy vynulují a při sec > 30 se sekundy nastaví na 59. Tím se nemusí řešit zda se minuty změní nebo ne. Korekce nebude nutná často, neboť DS3231 je velmi přesný a odchylka je za měsíc u mého kusu menší než 2 sec |
111-120 | Řeší se změna na letní/zimní čas. Je tu použit label = leto aby se zamezilo nesprávnostem při přechodu na zimní čas. Když se posune čas o hodinu zpět, tak by se za hodinu posunoval znova |
170-199 | Je měření teploty – převzato z WEBu. Je nutná drobná korekce teploty, protože uvnitř hodin je tepleji. Teploměr jsem tam přidal protože bylo k dispozici pět voltmetrů z ruského přístroje z roku 1950. |
Kód by se dal jistě ještě vylepšit. Převzaté části, když chodily, jsem nezkoumal. Sériová komunikace by se také dala vypustit, slouží jen pro oživení. Dalo by se doplnit ještě mnoho funkcí. Například tikání, odbíjení hodin, zvonkohra a podobně. Chce to jen čas. 🙂
Děkujeme za sdílení velice povedeného projektu! Další fotky si můžete prohlédnout zde.
- Sledovač slunce s Arduinem - 23.3.2022
- Programovatelný kytarový pedál s Arduinem - 26.2.2020
- Arduino infračervený teploměr vytištěný na 3D tiskárně - 11.2.2020