#!/usr/bin/python
# -*- encoding: utf-8 -*-

# cteni a zapis casu do RTC obvodu BQ32000
# posledni uprava 2.6.2013


import smbus
import time
import datetime
import os
import sys

bus = smbus.SMBus(1)  # novejsi varianta RasPi (512MB)
#bus = smbus.SMBus(0)  # starsi varianta RasPi (256MB)

adresa= 0x68          # I2C adresa RTC obvodu je pevně daná - 0x68 


#=============================================
# podprogram pro zjisteni casu v obvodu RTC a jeho zobrazeni, nebo nastavení casu v RasPi podle RTC
def rtc_info(nastav_cas=False):
  data =  bus.read_i2c_block_data(adresa,0x00) # okamzite nacteni vsech registru z obvodu
  datcas = datetime.datetime.utcnow()   # datcas bude obsahovat aktualni datum a cas v UTC (GMT)
  print "Systemovy cas v RasPi :" , datcas , "UTC"

  sek1  = data[0] & 0b00001111      # spodni 4 bity v nultem registru jsou jednotky sekund
  sek10 = data[0] & 0b01110000      # horni 3 bity v nultem registru jsou desitky sekund
  sek10 = sek10 >> 4                # posun desitek sekund o 4 pozice vpravo
  sekundy = (sek10 * 10) + sek1
  if (data[0] > 0b01111111):        # kdyz je nejvyssi bit nastaven na "1", RTC je zastavene
    print "RTC obvod je v rezimu STOP!"


  min1  = data[1] & 0b00001111      # spodni 4 bity v prvnim registru jsou jednotky minut
  min10 = data[1] & 0b01110000      # horni 3 bity v prvnim registru jsou desitky minut
  min10 = min10 >> 4                # posun desitek minut o 4 pozice vpravo
  minuty = (min10 * 10) + min1
  if (data[1] > 0b01111111):        # kdyz je nejvyssi bit nastaven na "1", doslo k nejake chybe oscilatoru
    print "Ziskane casove udaje jsou nespolehlive!"     # (napriklad vybiti zalozni baterie, nebo zastaveni hodin)


  hod1  = data[2] & 0b00001111      # spodni 4 bity v druhem registru jsou jednotky hodin
  hod10 = data[2] & 0b00110000      # horni 2 bity v druhem registru jsou desitky hodin 
  hod10 = hod10 >> 4                # posun desitek hodin o 4 pozice vpravo
  hodiny = (hod10 * 10) + hod1
  cent_en = data[2] & 0b10000000    # znacka, ktera udava, jestli je povoleno pocitat stoleti
  cent  = data[2] & 0b01000000      # pocitadlo stoleti (muze mit hodnotu 64 nebo 0)
  if (cent_en == 128):              # kdyz je povoleno pocitani stoleti
    if (cent == 0):                 #   a kdyz je pocitadlo na nule
      rok100 = 1900                 #       tak vnitrni datum udava rok 19xx
    else:                           #   kdyz neni pocitadlo na nule
      rok100 = 2000                 #       vnitrni datum je soucasne stoleti (20xx)
  else:                             # kdyz neni pocitadlo stoleti povoleno
    rok100 = 2000                   #   bere se to tak, ze je aktualni stoleti (20xx)


  rok1  = data[6] & 0b00001111      # spodni 4 bity v sestem registru jsou jednotky roku
  rok10 = data[6] & 0b11110000      # horni 4 bity v sestem registru jsou desitky roku 
  rok10 = rok10 >> 4                # posun desitek roku o 4 pozice vpravo
  roky = rok100 + (rok10 * 10) + rok1  # soucet dilcich udaju o roku


  dvt=["-----","nedele", "pondeli" , "utery" , "streda" , "ctvrtek" , "patek" , "sobota"]
  den_v_tydnu  = data[3] & 0b00000111  # spodni 3 bity v tretim registru jsou dny v tydnu
  nazevdne = dvt[den_v_tydnu]


  den1  = data[4] & 0b00001111      # spodni 4 bity ve ctvrtem registru jsou jednotky dni
  den10 = data[4] & 0b00110000      # horni 2 bity ve ctvrtem registru jsou desitky dni 
  den10 = den10 >> 4                # posun desitek dni o 4 pozice vpravo
  dny = (den10 * 10) + den1


  mes1  = data[5] & 0b00001111      # spodni 4 bity v patem registru jsou jednotky mesicu
  mes10 = data[5] & 0b00010000      # dalsi bit v patem registru jsou desitky mesicu 
  mes10 = mes10 >> 4                # posun desitek mesicu o 4 pozice vpravo
  mesice = (mes10 * 10) + mes1


  kalibrace = data[7] & 0b00011111  # spodnich 5 bitu ma v sobe kalibracni konstantu pro oscilator
  cal_sig   = data[7] & 0b00100000  # znamenko kalibracni konstanty
  if (cal_sig == 0b00100000):       # pri zapornem cisle se kalibraci obraci znamenko
    kalibrace = - kalibrace
  

  print "Datum a cas v RTC: " + str(dny) + "." + str(mesice) + "." + str(roky) + " " + str(hodiny)  + ":" + str(minuty) + ":" + str(sekundy) + " " + nazevdne  + " UTC"
  print "Kalibracni casova konstanta = " + str(kalibrace) + " (" + ppm(kalibrace)  + ")"

  if (nastav_cas == True):  # pokud je parametr tohoto podprogramu True, nastavi se RasPi podle RTC
    print "Cas v RasPi byl nastaven na:"
    prikaz="sudo date -u " + (str(mesice).rjust(2,"0") +
                              str(dny).rjust(2,"0") +
                              str(hodiny).rjust(2,"0") + 
                              str(minuty).rjust(2,"0") + 
                              str(roky) + "." + 
                              str(sekundy).rjust(2,"0"))
    os.system (prikaz)


#=============================================
# podprogram pro ulozeni systemoveho casu z RasPi do RTC obvodu
def rtc_zapis():
  datcas = datetime.datetime.utcnow()   # datcas bude obsahovat aktualni datum a cas v UTC (GMT)
  print "Systemovy cas v RasPi :" , datcas , "UTC"

  sek10 = int(datcas.second / 10)                                 # cele desitky sekund
  sek1 = datcas.second - (10 * sek10)                             # jednotky sekund
  stopbit = 0          # stopbit nastavit na normalni beh (pri 128 by se RTC zastavil)
  bus.write_byte_data(adresa , 0 ,stopbit + (sek10 << 4) + sek1)  # ulozit do registru 0


  min10 = int(datcas.minute / 10)                                 # cele desitky minut
  min1 = datcas.minute - (10 * min10)                             # jednotky minut
  osc_fail = 0          # vymazat chybu oscilatoru (vznika treba pri nizkem zaloznim napeti)
  bus.write_byte_data(adresa , 1 ,osc_fail + (min10 << 4) + min1) # ulozit do registru 1


  hod10 = int(datcas.hour / 10)                                   # cele desitky hodin
  hod1 = datcas.hour - (10 * hod10 )                              # jednotky hodin
  cent_en = 0b10000000                                            # povolit pocitani stoleti
  if (datcas.year < 2000):               # kdyz je aktualni rok mensi nez 2000 (nepravdepodobne)
    cent = 0                             #   nastavi se bit znacky stoleti na "0"
  else:                                  # jinak ...
    cent = 0b01000000                    # ... se nastavi bit znacky stoleti na "1"
  bus.write_byte_data(adresa , 2 ,cent_en + cent + (hod10 << 4) + hod1)   # ulozit do registru 2


  dvt = datetime.datetime.utcnow().weekday()       # zjisteni dne v tydnu (je to posunute - Po=0 ... Ne=6)
  preindexovani = dvt + 2                # posunuti dni v tydnu na spravne indexy
  if (preindexovani > 7):                # (z 0 na 2, z 1 na 3, .... ze 4 na 6, z 5 na 7, z 6 na 1)
    preindexovani = 1
  bus.write_byte_data(adresa , 3 ,preindexovani)   # ulozit do registru 3


  den10 = int(datcas.day / 10)                                    # cele desitky dni
  den1 = datcas.day - (10 * den10)                                # jednotky dni
  bus.write_byte_data(adresa , 4 , (den10 << 4) + den1)           # ulozit do registru 4


  mes10 = int(datcas.month / 10)                                  # cele desitky mesicu
  mes1 = datcas.month - (10 * mes10)                              # jednotky mesicu
  bus.write_byte_data(adresa , 5 , (mes10 << 4) + mes1)           # ulozit do registru 5



  rok100 = int(datcas.year / 100)                                 # orezani tisicu a stovek let
  rok100 =  datcas.year - (100 * rok100)

  rok10 = int(rok100 / 10)                                        # cele desitky roku
  rok1 = rok100 - (10 * rok10)                                    # jednotky roku
  bus.write_byte_data(adresa , 6 , (rok10 << 4) + rok1)           # ulozit do registru 6



# registry pro konfiguraci nabijeni baterie (kondenzatoru) - viz katalogovy list
  bus.write_byte_data(adresa , 8 , 0)            # 0=nenabijet ("TCH2" je rozpojene)
#  bus.write_byte_data(adresa , 8 , 0b00100000)   # 0b00100000 = sepnout "TCH2"

  bus.write_byte_data(adresa , 9 , 0)            # 0=nenabijet ("TCHE" je rozpojene)
#  bus.write_byte_data(adresa , 9 , 0b00000101)   # 0b00000101 = nabijet pres diodu a odpor 1k
#  bus.write_byte_data(adresa , 9 , 0b01000101)   # 0b01000101 = nabijet pres odpor 20k



  print "Systemovy cas byl prekopirovan do RTC obvodu."

 

#=============================================
#podprogram pro nastaveni vystupu IRQ na generovani frekvence 1Hz, nebo 512Hz
def frekvence (freq):

  registr7 = bus.read_byte_data(adresa , 7)   # nacte cely bajt z registru 7 (kvuli zachovani kalibrace)
  registr7 = registr7 | 0b01000000            # bit 6 nastavit na "1" (spusteni generatoru)
  bus.write_byte_data(adresa , 7 , registr7)  # upravenou hodnotu ulozit zpatky do registru 7

  if (freq == 1):
    klice=[0x5E,0xC7,0x01]                    # frekvenci generatoru nastavit na 1Hz
    bus.write_i2c_block_data(adresa,0x20,klice)
    print "vystup IRQ kmita frekvenci 1Hz"
  else:
    klice=[0x5E,0xC7,0x00]                    # frekvenci generatoru nastavit na 512Hz
    bus.write_i2c_block_data(adresa,0x20,klice)
    print "vystup IRQ kmita frekvenci 512Hz"



#=============================================
#podprogram pro nastaveni vystupu IRQ na trvalou "1" nebo "0"
def vystup (stav):
  registr7 = bus.read_byte_data(adresa , 7)   # nacte cely bajt z registru 7 (kvuli zachovani kalibrace)
  registr7 = registr7 & 0b00111111            # bity 6 a 7 se vynuluji (IRQ se prepne na "0")

  if (stav == True):
    registr7 = registr7 | 0b10000000          # bit 7 na "1"  (IRQ se prepne na "1")
    print "IRQ vystup byl nastaven na '1'"
  else:
    print "IRQ vystup byl nastaven na '0'"

  bus.write_byte_data(adresa , 7 , registr7)  # upravenou hodnotu ulozit zpatky do registru 7



#=============================================
#podprogram na presne zkalibrovani krystalu (jemne doladeni frekvence)
def naladit (konstanta):
  registr7 = bus.read_byte_data(adresa , 7)   # nacte cely bajt z registru 7 (kvuli zachovani stavu IRQ)

  puvodni_k = registr7 & 0b00011111           # zjisteni stare konstanty (na bitech 0 az 4) 
  puvodni_znamenko = registr7 & 0b00100000    # zjisteni znamenka stare konstanty (v bitu 5) 
  if (puvodni_znamenko == 0b00100000 ):       # kdyz je znamenko zaporne,
     puvodni_k = -puvodni_k                   #      je konstanta zaporna

  print "puvodni casova kalibracni konstanta byla :" , puvodni_k

  registr7 = registr7 & 0b11000000            # smazani stare konstanty (na bitech 0 az 5)

  if (konstanta < 0):
    registr7 = registr7 | 0b00100000          # znamenkovy bit na "1" (zaporne cislo)
    konstanta = abs(konstanta)                # prevedeni zaporne konstanty na kladne cislo
    nove_znamenko = "-"
  else:
    nove_znamenko = "+"

  if (konstanta > 31):                        # testovani na prekroceni povoleneho rozsahu -31 az +31
    print "zadano moc velke cislo ... automaticky nahrazeno 0"
    konstanta = 0
    nove_znamenko = ""

  registr7 = registr7 | konstanta             # nova konstanta se prida k hodnote registru
  bus.write_byte_data(adresa , 7 , registr7)  # upravenou hodnotu ulozit zpatky do registru 7

  print "konstanta byla zmenena na :" , nove_znamenko + str(konstanta)



#=============================================
# podprogram pro vypsani hodnot vsech registru v obvodu RTC
def rtc_registry():
  data =  bus.read_i2c_block_data(adresa,0x00) # okamzite nacteni 31 registru z obvodu

  # prevod hodnot registru na binarni hodnotu doplnenou na delku 8 bitu nulami pred cislem
  # zaroven se z binarniho cisla vyhodi prvni 2 znaky ("0b")
  binreg0 = str(bin(data[0])[2:].zfill(8))
  binreg1 = str(bin(data[1])[2:].zfill(8))
  binreg2 = str(bin(data[2])[2:].zfill(8))
  binreg3 = str(bin(data[3])[2:].zfill(8))
  binreg4 = str(bin(data[4])[2:].zfill(8))
  binreg5 = str(bin(data[5])[2:].zfill(8))
  binreg6 = str(bin(data[6])[2:].zfill(8))
  binreg7 = str(bin(data[7])[2:].zfill(8))
  binreg8 = str(bin(data[8])[2:].zfill(8))
  binreg9 = str(bin(data[9])[2:].zfill(8))
  # ... dalo by se to napsat lepe (nejakou smyckou), ale tohle je trochu prehlednejsi

  # zobrazeni vsech udaju v hezky upravenych sloupeccich 
  print "reg  dec   bin     rozlozene bity       popis"
  print "0x00" , str(data[0]).zfill(3),  "=" , binreg0,  "=" , binreg0[0:1] + " " + binreg0[1:4] + " " + binreg0[4:8] , " (STOP_bit , desitky sekund , jednotky sekund)"
  print "0x01" , str(data[1]).zfill(3),  "=" , binreg1,  "=" , binreg1[0:1] + " " + binreg1[1:4] + " " + binreg1[4:8] , " (FAIL_bit , desitky minut , jednotky minut)"
  print "0x02" , str(data[2]).zfill(3),  "=" , binreg2,  "=" , binreg2[0:1] + " " + binreg2[1:2] + " " + binreg2[2:4] + " " + binreg2[4:8] , "(CENT_EN , stoleti , desitky hodin , hodiny)"
  print "0x03" , str(data[3]).zfill(3),  "=" , binreg3,  "=" , "xxxxx " + binreg3[5:8] , "  (den v tydnu: 1=Ne, 2=Po, 3=Ut ... 7=So)"
  print "0x04" , str(data[4]).zfill(3),  "=" , binreg4,  "=" , "xx " + binreg4[2:4] + " " + binreg4[4:8] , " (desitky dni , jednotky dni)"
  print "0x05" , str(data[5]).zfill(3),  "=" , binreg5,  "=" , "xxx " + binreg5[3:4] + " " + binreg5[4:8] , " (desitky mesicu , jednotky mesicu)"
  print "0x06" , str(data[6]).zfill(3),  "=" , binreg6,  "=" , binreg6[0:4] + " " + binreg6[4:8]  , "  (posledni dvojcisli roku)"
  print "0x07" , str(data[7]).zfill(3),  "=" , binreg7,  "=" , binreg7[0:1] + " " + binreg7[1:2] + " " + binreg7[2:3] + " " + binreg7[3:8] , "(OUT, FT, znamenko , kalibrace)"
  print "0x08" , str(data[8]).zfill(3),  "=" , binreg8,  "=" , "xx " + binreg8[2:3] + " xxxxx" , " (Nabijeni- bit TCH2)"
  print "0x09" , str(data[9]).zfill(3),  "=" , binreg9,  "=" , "x " + binreg9[1:2] + " xx " + binreg9[4:8] , "(Nabijeni- bit TCFE a kod TCHE)"
  print " "
  data =  bus.read_i2c_block_data(adresa,0x20)     # okamzite nacteni zbylych registru (od cisla 32)
  print "0x20" , str(data[0]).zfill(3),  "=" ,  bin(data[0])[2:].zfill(8) , "(klic1 pro SFR registr)"
  print "0x21" , str(data[1]).zfill(3),  "=" ,  bin(data[1])[2:].zfill(8) , "(klic2 pro SFR registr)"
  print "0x22" , str(data[2]).zfill(3),  "=" ,  bin(data[2])[2:].zfill(8) , "(SFR registr-prepinac frekvence IRQ je na nejnizsim bitu)"



#=============================================
# podprogram pro zastavovani a spousteni hodin
def zastavit(stav):                          # (pri 1 se hodiny zastavi, pri 0 se hodiny spusti)
  registr0 = bus.read_byte_data(adresa , 0)       # nacte cely bajt z registru 0
  stary_stopbit = registr0  >> 7                  # z nej se zjisti aktualni hodnota STOP_bitu (nejvyssi bit) 
  if (stary_stopbit == stav):                     # kdyz se aktualni a nove pozadovany STOP_bit shoduji
    print "Nedoslo ke zmene stavu STOP bitu,"     #   nic se nedeje
    if (stary_stopbit == 1):                      #   jen se zobrazi informace o aktualnim stavu
       print "  RTC obvod stale stoji."
    else:
       print "  RTC obvod stale bezi."

  else:                                           # pokud se aktualni a novy STOP_bit neshoduje
    if (stav == 1):                               #   nastavi se podle noveho pozadavku
       registr0 = registr0 | 0b10000000           #     (bud na '1')
       print "RTC obvod byl zastaven"
    else:
       registr0 = registr0 & 0b01111111           #     (nebo na '0')
       print "RTC obvod byl spusten"
    bus.write_byte_data(adresa , 0 , registr0)    # a upravena hodnota se ulozi zpatky do registru 0
    


#=============================================
# podprogram pro prepocet kalibracni konstanty na PPM (podle katalogoveho listu)
def ppm(konstanta):   

  if (konstanta == 0):
    ppmstr = "bez korekce frekvence"
  elif (konstanta > 0):
    ppmstr = "zpomaleni RTC o " + str(int(konstanta / 0.491520)) + " ppm"
  else:
    ppmstr = "zrychleni RTC o " + str(int(abs(konstanta) / 0.245760)) + " ppm"

  return ppmstr



#=============================================
# podprogram pro nastaveni casu v RasPi podle NTP serveru na internetu 
def ntp():   

  print "probiha restart NTP"
  prikaz="sudo /etc/init.d/ntp restart"
  os.system (prikaz)                         # spusteni prikazu v BASHi





#=============================================
#  ***** hlavni program *****
#=============================================



# vyhodnoceni predavaneho parametru pri spousteni programu
try:                      # odchytavani chyby, ktera by vznikla pri chybejicim parametru
  akce = sys.argv[1]      # parametr spousteneho programu do promenne "akce"
except:                   # kdyz parametr chybi, je nahrazen prazdnym retezcem
  akce=""

akce = akce.lower()       # prevod parametru na mala pismena

if (akce == "-i"):        # rozeskoky pri ruznych akcich
  rtc_info()
elif (akce == "-pi2rtc"):
  rtc_zapis()
elif (akce == "-rtc2pi"):
  rtc_info(True)
elif (akce == "-ntp2pi"):
  ntp()
elif (akce == "-reg"):
  rtc_registry()
elif (akce == "-f1"):
  frekvence(1)
elif (akce == "-f512"):
  frekvence(512)
elif (akce == "-out1"):
  vystup(True)
elif (akce == "-out0"):
  vystup(False)
elif (akce == "-stop"):
  zastavit(1)
elif (akce == "-start"):
  zastavit(0)
elif (akce == "-k"):
  try:                             # odchytavani chyby, ktera by vznikla pri chybejicim parametru
    konstanta = int(sys.argv[2])   # hodnota zadane kalibracni konstanty do promenne "konstanta"
  except:                          # kdyz hodnota chybi, nebo je to text, nastaví se konstanta na 0
    konstanta = 0
  naladit(konstanta)

else:                              # pri jakemkoli jinem parametru se zobrazi napoveda
  print "Pripustne parametry:"
  print "... -i        INFO - Jen zobrazi cas v RasPi a v RTC"
  print "... -pi2rtc   Zapise cas z RasPi do RTC"
  print "... -rtc2pi   Zapise cas z RTC do RasPi"
  print "... -ntp2pi   Restartuje NTP - seridi cas v RasPi podle internetu"
  print "... -reg      Vypise aktualni stav vsech registru"
  print "... -f1       Nastavi IRQ generator na 1Hz"
  print "... -f512     Nastavi IRQ generator na 512Hz"
  print "... -out0     Nastavi IRQ na '0'"
  print "... -out1     Nastavi IRQ na '1'"
  print "... -stop     Zastavi hodiny"
  print "... -start    Spusti zastavene hodiny"
  print "... -k xnn    Nastavi kalibracni konstantu na hodnotu 'xnn' (-31 az +31)"
  print "                    x je znamenko ('-' zrychluje RTC; '+' zpomaluje RTC)"
  print "-------------"
  print "Priklady:"
  print "  sudo python /home/pi/bq32000.py -k -15  ....... zmena konstanty na -15"
  print "  sudo python /home/pi/bq32000.py -k +3   ......... zmena konstanty na 3"
  print "  sudo python /home/pi/bq32000.py -f512   .... vystup IRQ rozkmita 512Hz"
  



