Persukdamas savo elektromechaninį laikrodį į vasaros laiką, pastebėjau, kad laikrodis skuba daugiau nei viena minute. Kadangi laikrodis surinktas kreivai – šleivai, gali būti, kad RTC veikimą įtakoja kokia nors elektromagnetinė interferencija ar pan. Per daug į tai gilintis patingėjau, nes prisiminiau, kad prie laikrodžio kaip tyčia yra prijungtas HC05 BlueTooth moduliukas, kuris leidžia laikrodžio laiką atnaujinti nuotoliu būdu, o tai yra puiki galimybė laiko neatitikimą nuo realybės išspręsti reguliaria laiko sinchronizacija su RPI, kuri savo ruožtu laiką gauna iš NTP serverio.
Pasiruošimas
Įkišam USB Bluetooth adapterį į RPI ir terminale įvykdome šias komandas:
sudo apt-get update sudo apt-get install bluetooth bluez-utils blueman
Įvykdytos komandos sudiegs visą bendravimui su bluetooth adapteriu reikalingą programinę įrangą. Norint įsitikinti, kad bluetooth modulis susidiegė teisingai, galima įvykdyti šią komandą:
hciconfig
Turėtume pamatyti kažką panašaus į:
Įsitikinti, ar bluetooth veikia galima ir taip:
sudo /etc/init.d/bluetooth status
Beje, tokiu pat būdu, iškilus poreikiui, galima sustabdyti, paleisti ir perkrauti bluetooth komunikatorių. Tam komandoje žodį „status„, atitinkamai, reiktų keisti „start”, „stop”, „restart” ar „force-reload„.
Jeigu bluetooth modulis veikia tvarkingai, galima susirasti aplinkinius įrenginius su komanda:
hcitool scan
Įrenginių sąraše turėtų matytis mus dominantis įrenginys. Norint su juo bendrauti, bluetooth įrenginius reikia suporuoti (angl. pair) su komanda:
sudo bluez-simple-agent hci0 xx:xx:xx:xx:xx:xx
Savaime suprantama, vietoje xx:xx:xx:xx:xx:xx reiktų nurodyti savo pasirinkto bluetooth įrenginio MAC adresą. Įvykdžius komandą bei teisingai įvedus įrenginio slaptažodį (PIN kodą), įrenginiai turėtų susiporuoti ir būti pasiruošę tiesioginiam bendravimui. Norint su įrenginiu bendrauti nuosekliuoju protokolu (USART), reikia suderinti RFCOMM protokolo nustatymus:
sudo nano /etc/bluetooth/rfcomm.conf
Faile reikia paredaguoti šias eilutes, įrašant į atitinkamą vietą savo pasirinkto bluetooth įrenginio MAC adresą:
rfcomm1 { bind yes; device xx:xx:xx:xx:xx:xx; channel 1; comment "Connection to Bluetooth serial module"; }
Išsaugojus failą, įvykdome komandą, kuri sukurs virtualų nuoseklųjį prievadą (angl. serial port) /dev/rfcomm1, per kurį bus galima bendrauti su pasirinktu įrenginiu, mano atveju su flip-dot laikrodžiu:
sudo rfcomm bind all
Pasiruošimas kaip ir baigtas, liko tik susidiegti pySerial biblioteką, kuri leis siųsti ir priimti duomenis nuosekliuoju prievadu.
sudo apt-get install python-serial
Python skriptas
Po ilgų išieškojimų gavosi štai toks nepadoriai ilgas python skriptas (veikimo diagrama viršuje):
import sys import arrow import re import serial import os import time import datetime from time import sleep from serial import SerialException os.chdir(os.path.abspath('/home/python/')) f = open('flip-dot_synch.log','a') sys_date = arrow.now().format('YYYY-MM-DD HH:mm:ss') s = str(sys_date + ' Flip-Dot Synch: \n') f.write(s) #status variables connected = 0 update = 0 class bcolors: #terminal text color HEADER = '\033[95m' OKBLUE = '\033[94m' OKGREEN = '\033[92m' WARNING = '\033[93m' FAIL = '\033[91m' ENDC = '\033[0m' BOLD = '\033[1m' UNDERLINE = '\033[4m' #config variables max_seconds_diff = 20 print bcolors.HEADER + "Flip-Dot Clock auto-config V1.0" + bcolors.ENDC try: bluetoothSerial = serial.Serial( "/dev/rfcomm1", baudrate=9600, timeout=1, stopbits = serial.STOPBITS_ONE, bytesize = serial.EIGHTBITS) bluetoothSerial.setRTS(0) connected=1 except serial.SerialException: print bcolors.FAIL + "No connection to the device could be established" + bcolors.ENDC f.write(" No connection to the device could be established! \n") f.close() exit() quit() if connected: bluetoothSerial.flushInput() #flush input buffer, discarding all its contents bluetoothSerial.flushOutput()#flush output buffer, aborting current output bluetoothSerial.write('x') #quit any existing flip-dot clock config mode time.sleep(5) eilute = "" sys_date = arrow.now().format('YYYY-MM-DD HH:mm:ss') flipdot_date = arrow.get('1994-09-01 08:00:00', 'YYYY-MM-DD HH:mm:ss') bluetoothSerial.write( 'a' ) time.sleep(1) for x in range (0,10): eilute = str(bluetoothSerial.readline()) mat=re.findall(r"(\d+[-]\d+[-]+\d+\s+\d+[:]\d+[:]+\d*)", str(eilute)) if mat: flipdot_date = arrow.get(str(mat), 'YYYY-MM-DD HH:mm:ss') print " " if flipdot_date != arrow.get('1994-09-01 08:00:00', 'YYYY-MM-DD HH:mm:ss'): print "Clock time: %s" %flipdot_date.format('YYYY-MM-DD HH:mm:ss') print "System time: %s" %sys_date.format('YYYY-MM-DD HH:mm:ss') fd = str(arrow.get(flipdot_date).format('YYYY-MM-DD HH:mm:ss')) s = str(' Clock time: '+ fd +'\n') f.write(s) sd = str(arrow.get(sys_date).format('YYYY-MM-DD HH:mm:ss')) s = str(' System time: '+ sd +'\n') f.write(s) time_difference=abs((arrow.get(flipdot_date) - arrow.get(sys_date)).total_seconds()) print "Difference: %s seconds" %time_difference s = str(' Difference: '+ str(time_difference) +'\n') f.write(s) if time_difference > max_seconds_diff: print " " print (bcolors.WARNING + "Difference is bigger then %s seconds.." + bcolors.ENDC) %max_seconds_diff print "Attempting to update.." update = 1 # time needs to be synchronized else: print " " print bcolors.OKGREEN + "Everything seems OK. Bay!" + bcolors.ENDC f.write(" Everything seems OK. Bay!\n") else: print "Could not read flip-dot clock" f.write(" Could not read flip-dot clock \n") bluetoothSerial.write( 'x' ) time.sleep(0.1) f.close() exit() quit() # Testavimui # update = 1 # time_difference = 50 # Going to time/date update sequence: if update == 1: # --------------------------- DATE update sequence ------------------------- if time_difference >= 24*60*60: #date needs to be updated print ("%s Date update sequence [" + bcolors.OKBLUE + "start" + bcolors.ENDC + "]") %str(arrow.now().format('HH:mm:ss')) bluetoothSerial.write('t') time.sleep(1) bluetoothSerial.flushInput() #flush input buffer, discarding all its contents bluetoothSerial.flushOutput()#flush output buffer, aborting current output bluetoothSerial.write(str(arrow.now().format('YY'))) bluetoothSerial.write('\n') time.sleep(0.5) bluetoothSerial.write(str(arrow.now().format('MM'))) bluetoothSerial.write('\n') time.sleep(0.5) bluetoothSerial.write(str(arrow.now().format('DD'))) bluetoothSerial.write('\n') time.sleep(0.5) bluetoothSerial.write( 'y' ) time.sleep(0.1) for x in range (0,10): eilute = str(bluetoothSerial.readline()) mat=re.findall(r'Success!|Error!?', str(eilute)) if mat: if str(mat)==str("['Success!']"): print ("%s Date update sequence [" + bcolors.OKGREEN + "success" + bcolors.ENDC + "]") %str(arrow.now().format('HH:mm:ss')) sys_date = arrow.now().format('YYYY-MM-DD HH:mm:ss') s = str(' Date update sequence [success] ' + sys_date + '\n') f.write(s) else: print ("%s Date update sequence [" + bcolors.FAIL + "error" + bcolors.ENDC + "]") %str(arrow.now().format('HH:mm:ss')) sys_date = arrow.now().format('YYYY-MM-DD HH:mm:ss') s = str(' Date update sequence [error] ' + sys_date + '\n') f.write(s) time.sleep(1) else: print bcolors.OKGREEN + "Date does not need to be updated!" + bcolors.ENDC f.write(' Date does not need to be updated! \n') # --------------------------- TIME update sequence ------------------------- print ("%s Time update sequence [" + bcolors.OKBLUE + "start" + bcolors.ENDC + "]") %str(arrow.now().format('HH:mm:ss')) bluetoothSerial.write('t') time.sleep(1) bluetoothSerial.flushInput() #flush input buffer, discarding all its contents bluetoothSerial.flushOutput()#flush output buffer, aborting current output bluetoothSerial.write(str(arrow.now().format('HH'))) bluetoothSerial.write('\n') time.sleep(0.5) bluetoothSerial.write(str(arrow.now().format('mm'))) bluetoothSerial.write('\n') time.sleep(0.5) bluetoothSerial.write(str(arrow.now().format('ss'))) bluetoothSerial.write('\n') time.sleep(0.5) bluetoothSerial.write( 'y' ) time.sleep(0.1) for x in range (0,10): eilute = str(bluetoothSerial.readline()) # print eilute mat=re.findall(r'Success!|Error!?', str(eilute)) #\bSuccess!?\b if mat: if str(mat)==str("['Success!']"): print ("%s Time update sequence [" + bcolors.OKGREEN + "success" + bcolors.ENDC + "]") %str(arrow.now().format('HH:mm:ss')) sys_date = arrow.now().format('YYYY-MM-DD HH:mm:ss') s = str(' Time update sequence [success] ' + sys_date + '\n') f.write(s) else: print ("%s Time update sequence [" + bcolors.FAIL + "error" + bcolors.ENDC + "]") %str(arrow.now().format('HH:mm:ss')) sys_date = arrow.now().format('YYYY-MM-DD HH:mm:ss') s = str(' Time update sequence [error] ' + sys_date + '\n') f.write(s) time.sleep(3) bluetoothSerial.write( 'x' ) time.sleep(0.1) f.close() exit() quit() else: #noting needs to be updated, can quit now f.close() exit() quit() else: print bcolors.FAIL + "I said, I was not connected!" + bcolors.ENDC f.write('I said, I was not connected! \n') f.close() exit() quit() bluetoothSerial.write( 'x' ) time.sleep(0.1) bluetoothSerial.close() f.write('Normal exit. \n') f.close() exit() quit()
Skirptas gavosi toks ilgas todėl, kad jis laikrodžio laiką ir datą atnaujina ne aklai, tačiau tik tada, kai to reikia, t. y. kai laikrodžio laikas nesutampa su RPI laiku per max_seconds_diff = 20 sekundžių. Tam teko realizuoti aibę visokių papildomų patikrinimų ir apsaugų nuo timeout’ų ir blogo suveikimo. Taip pat skripte realizavau ir logo rašymą į failą flip-dot_synch.log, kuris labai praverčia skriptą leidžiant cron priemonėmis:
30 2,4 * * * sudo python /home/python/flip-dot_synch.py &
Beje, sukurtus logus terminale patogu skaityti su komanda „less”:
less /home/python/flip-dot_synch.log
Atsisiuntimai
Tie, kas tingi kopijuoti skpripto kodą, gali atsisiųsti viską skriptą iš čia: flip-dot_synch.zip
Naujausią kodo versiją rasite mano GitHub saugykloje!
Naudinga literatūra:
- http://www.uugear.com/portfolio/bluetooth-communication-between-raspberry-pi-and-arduino/
- http://blog.dawnrobotics.co.uk/2013/11/talking-to-a-bluetooth-serial-module-with-a-raspberry-pi/
- http://www.modmypi.com/blog/installing-the-raspberry-pi-nano-bluetooth-dongle
- https://www.coolcomponents.co.uk/article/the-stalker-sensor-platform-part-5-logging-data-using-raspberry-pi
- http://blog.davidvassallo.me/2014/05/11/android-linux-raspberry-pi-bluetooth-communication/