Skip to main content

7.7 Budilica

Ovaj projekt je prava budilica. Pločica se pridružuje tvojoj Wi-Fi mreži, pita vremenski server na internetu koliko je sati, i prikazuje to na LCD-u. Dvije tipke postavljaju alarm, a kada sat dođe do njega, buzzer se oglasi.

To je najveći sklop u kitu, i gotovo ništa u njemu nije novo. LCD je spojen točno kao u 4.1 Prikaz poruke na LCD-u, tipke se čitaju točno kao u 2.2 Debounce tipke, buzzer se pobuđuje točno kao u 2.4 Piskanje buzzera, a mrežna veza je ona iz 6.1 Povezivanje i dohvat podataka. Novo je to što sva četiri rade u isto vrijeme.

U ovoj dokumentaciji naučit ćeš:

  • Kako pločica bez vlastitog sata nauči koliko je sati, pomoću NTP-a
  • Zašto vrijeme na tvom ekranu ovisi o pomaku vremenske zone koji moraš sam postaviti
  • Kako iz onoga što ti daje time.localtime() pročitati sat i minutu
  • Kako dopuniti broj nulom pomoću "{:02d}"
  • Kako operator % tjera brojač da obiđe na 24 i na 60
  • Kako dvije varijable sprječavaju alarm da se oglašava iznova cijelu minutu

Potreban hardver:

  • 1x Soldered NULA MINI pločica
  • 1x Breadboard
  • 1x Soldered Qwiic 16x2 LCD ekran
  • 2x Tipke
  • 1x Pasivni buzzer
  • 10x Jumper žice
  • 1x Qwiic kabel
  • 1x USB-C kabel
ℹ️
Ovom primjeru ne trebaju otpornici uopće. Tipke koriste otpornike ugrađene u čip, uključene u kodu pomoću Pin.PULL_UP, a pin buzzera se uvijek kreće samo između 0 V i 3.3 V pa ga nema od čega štititi. Ako si vidio krug budilice nacrtan s dva otpornika od 10 kΩ, to je drugačiji način spajanja tipki i nije ono što ova skripta očekuje.
⚠️
Pobrini se da u ruci držiš pasivni buzzer, a ne aktivni. Pasivni buzzer svira onu frekvenciju koja mu se pošalje, a na to se piskanje oslanja. Aktivni buzzer sadrži vlastiti oscilator i piska na jednoj fiksnoj visini bez obzira što kod traži.

Spajanje komponenti

Ovaj sklop koristi tri pina plus masu, i sva četiri su na f–j strani pločice:

PinRedStranaŠto radi
IO225f–jTipka za sate
IO326f–jTipka za minute
IO427f–jBuzzer
GND30f–jMasa za sve

LCD-u ne trebaju pinovi uopće. Spaja se preko Qwiic utičnice pločice, koja je interno spojena na IO6 i IO7.

⚠️
Red 27 postoji s obje strane pločice i ta dva su različiti pinovi. Na f–j strani red 27 je IO4, buzzer. Na a–e strani red 27 je IO19, koji ovaj projekt ne koristi. Sjede na suprotnim krajevima istog broja reda i nisu spojeni međusobno. Čitaj ime ispisano duž ruba pločice, nikada broj reda sam.

Tijelo pločice prekriva srednje stupce, pa su uz nju slobodni samo stupac a na a–e strani i stupac j na f–j strani. Svaka žica koja dotiče pločicu ide zato u a… ili j… rupicu.

Prati jedanaest koraka ispod. Svaka fotografija snimljena je s istog položaja, pa svaku možeš usporediti s onom prije nje i vidjeti točno što se promijenilo.

1. Umetni NULA MINI pločicu na breadboard

ℹ️
Ovaj korak pretpostavlja da znaš kako je breadboard spojen iznutra i što su njegove naponske vodilice (power rails). Za uvod pogledaj stranicu dokumentacije Osnove breadboarda.

Utisni pločicu u jedan kraj breadboarda tako da njezina dva reda pinova sjednu s obje strane središnjeg kanala, s čipom okrenutim prema dolje. Pritisni je ravnomjerno dok svi pinovi ne sjednu.

NULA MINI pločica postavljena na jedan kraj breadboarda
Korak 1: pločica postavljena na breadboard, zauzima redove 25 do 30

Na fotografijama koje slijede pločica zauzima redove 25 do 30.

ℹ️
Tvoja pločica može sjediti u drugim redovima od one na fotografijama. Važno je u koji red sleti koji pin. Čitaj imena ispisana duž rubova pločice, a ne prepisuj brojeve.

2. Sagradi jednu dijeljenu masu

Masa je najzaposleniji spoj u ovom projektu: trebaju je i obje tipke i buzzer. Zato je prvi posao pretvoriti dvije plave vodilice u jednu liniju mase do koje obje strane pločice mogu doći.

Za to treba četiri kratke jumper žice:

IzUŠto radi
j30 (GND)f–j plava vodilicaIzvodi masu s pločice
j1f–j plava vodilicaVraća masu na red 1
e1f1Prenosi je preko središnjeg kanala
a1a–e plava vodilicaDovodi je u drugu vodilicu
Četiri plave jumper žice spajaju masu pločice na obje plave negativne vodilice preko reda 1
Korak 2: masa izvedena s pločice i prenesena okolo do daleke vodilice preko reda 1
ℹ️
Vodilice duž dva ruba breadboarda nisu spojene unutar njega: svaki pojas ide dužinom jednog ruba i tamo staje. Njihovo spajanje je ono što dopušta tipkama da masu uzimaju s bližeg ruba umjesto da vodiš duge žice natrag preko pločice. Red 1 se ovdje koristi kao kamen za prelaz samo zato što su jumper žice u kitu kratke; jedna dugačka žica oko kraja breadboarda radi točno isti posao.
ℹ️
Svaka žica vodilice mora ići u red rupica koji ide uz plavu liniju. Svaki rub breadboarda nosi dva reda vodilica, a onaj uz crvenu liniju je zaseban pojas koji nije spojen s njim.

3. Postavi dvije tipke

Utisni dvije tipke u sredinu breadboarda tako da svaka premosti središnji kanal, nekoliko redova dalje od pločice. Svaka tipka obuhvaća dva reda.

Na fotografijama sjede u redovima 17 i 19 za tipku sati, najbliža pločici, i redovima 13 i 15 za tipku minuta.

Dvije tipke postavljene preko središnjeg kanala u redovima 17 do 19 i 13 do 15
Korak 3: dvije tipke premošćuju središnji kanal
⚠️
Tipka ima četiri nožice, ali samo dvije su važne. Dvije nožice na istoj strani tipke trajno su spojene unutar nje. Pritisak tipke spaja jednu stranu s drugom. Zato za svaku tipku uvijek koristi jednu nožicu s a–e strane i jednu s f–j strane. Ako uzmeš obje žice s iste strane, te dvije su već spojene i pločica će se ponašati kao da je ta tipka zauvijek pritisnuta.

4. Spoji obje tipke na masu

Dvije kratke jumper žice, jedna po tipki, s a–e strane tipke do plave vodilice na tom rubu: a19 za tipku sati i a15 za tipku minuta.

Dvije jumper žice spajaju svaku tipku na plavu negativnu vodilicu na a-e rubu
Korak 4: obje tipke spojene na masu na plavoj − vodilici na a–e rubu
ℹ️
Ovdje se most iz koraka 2 isplati. Tipke masu uzimaju s a–e vodilice, ali je GND izveden s pločice na f–j strani. Bez tih žica preko reda 1 ta vodilica ne bi bila spojena ni na što, i obje tipke bile bi mrtve.

5. Spoji svaku tipku na svoj pin

Sada dvije signalne žice, obje na f–j strani. Svaka ide od pina pločice do nižeg reda para jedne tipke:

IzUTipkaBoja na fotografiji
j25 (IO2)j17SatiNarančasta
j26 (IO3)j13MinuteBijela
Narančasta i bijela jumper žica idu od IO2 i IO3 do dvije tipke
Korak 5: svaka tipka spojena na svoj pin, IO2 na tipku najbližu pločici

Primijeti da dvije žice idu paralelno i nikada se ne križaju. IO2, pin najbliži tipkama, ide na najbližu tipku. Njihova zamjena zamjenjuje što tipke rade: tipka sati postavljala bi minute a tipka minuta sate.

ℹ️
Svaka tipka sada ima masu s jedne strane središnjeg kanala i svoj pin s druge. To je isti dijagonalni par koji je za jednu tipku korišten u 2.1 Brojač pritisaka tipke, samo dvaput.

6. Postavi buzzer

Buzzer ide na f–j stranu na daleki kraj breadboarda, s nožicama u dva susjedna reda, na fotografiji redovi 7 i 8.

Pogledaj vrh njegovog kućišta prije nego ga utisneš. Uz utisnuta slova nalazi se mali + unutar kružića, i nožica na toj strani kućišta je pozitivna. Ta nožica ide u red 8, red bliži tipkama.

Pasivni buzzer utisnut u redove 7 i 8 na f-j strani breadboarda
Korak 6: buzzer u redovima 7 i 8, njegova ⊕ oznaka okrenuta prema strani reda 8
⚠️
Dvije nožice sjede u susjednim redovima, pa odvoji trenutak da provjeriš da su stvarno sletjele u dva različita reda. Ako obje nožice završe u istom redu, buzzer je kratko spojen i ostaje tih bez obzira što kod radi.

7. Spoji buzzer na IO4

Jedna jumper žica iz j27, a to je IO4, do j8, reda u kojem je + nožica buzzera. To je žica koja nosi pravokutni signal.

Zelena jumper žica ide od IO4 u j27 do pozitivne nožice buzzera u j8
Korak 7: IO4 spojen na + nožicu buzzera

8. Spoji buzzer na masu

Još jedna jumper žica, iz j7 (druga nožica buzzera) dolje do plave vodilice na istom rubu.

Plava jumper žica spaja drugu nožicu buzzera u j7 na plavu negativnu vodilicu
Korak 8: buzzer spojen na masu, čime se zatvara krug IO4 → buzzer → GND

Krug je sada zatvoren: IO4 → buzzer → GND. Kućište od 12 mm skriva nožice kada je buzzer utisnut, pa ako ikada budeš morao provjeriti ovo spajanje, čitaj umjesto toga dvije jumper žice.

Breadboard je gotov. Sve odsad se jednostavno utiskuje.

9. Spoji Qwiic kabel na pločicu

LCD uopće ne dotiče breadboard. Spaja se preko jednog jedinog Qwiic kabela, koji u jednom utikaču nosi napajanje i oba I2C signala.

Nađi bijelu Qwiic utičnicu na NULA MINI i utisni jedan kraj kabela u nju. Ulazi samo na jedan način.

Qwiic konektor istaknut na NULA MINI pločici
Qwiic konektor na NULA MINI
Qwiic kabel utisnut u NULA MINI s drugim krajem još slobodnim
Korak 9: Qwiic kabel u pločici, drugi kraj još slobodan

10. Spoji drugi kraj na LCD

Okreni ekran. Ljubičasta pločica na njegovoj pozadini je I2C LCD ADAPTER, i nosi dvije Qwiic utičnice, po jednu na svakom kraju. Bilo koja radi: to je isto spojište s dva utikača.

Drugi kraj Qwiic kabela utisnut u ljubičastu I2C LCD adapter pločicu
Korak 10: kabel utisnut u LCD adapter, sa silkscreenom I2C ADDR 0X20
ℹ️
I2C ADDR 0X20 ispisan na adapteru je adresa ekrana na sabirnici. Skripta je nikada ne spominje jer je driver već zna: LCD_I2C(i2c) ispuni 0x20 za tebe. Kontakti A0, A1 i A2 uz nju mogu tu adresu pomaknuti, a to bi koristio ako bi ikada htio dva ekrana na jednoj pločici.

11. Spoji pločicu na računalo

Utisni USB-C kabel u pločicu. PWR LED-ica se upali, a s njom i pozadinsko svjetlo ekrana.

Gotova budilica napajana preko USB-C, LCD upaljen i prikazuje red punih blokova
Korak 11: gotov sklop, napojen prije nego je ikakva skripta pokrenuta
⚠️
Red punih blokova u gornjoj liniji je u ovom trenutku normalan, a tako i običan plavi ekran. Oboje znači da ekran ima napajanje ali nije inicijaliziran, što je točno tako i treba, jer nijedna skripta još nije pokrenuta. Upaljeno pozadinsko svjetlo dokazuje samo da ekran prima napajanje; ne govori ništa o tome može li pločica s njim razgovarati. Tekst se pojavljuje kada pokreneš kod.

Kako pločica zna koliko je sati

NULA MINI nema vlastiti sat. Iskopčaj je i zaboravi koliko je sati. Nema baterije koja drži sat da otkucava kako je ima u ručnom satu ili u računalu.

Zato pita. Kada je na mreži, pošalje kratak zahtjev vremenskom serveru i dobije natrag trenutno vrijeme. Protokol za to je NTP, Network Time Protocol, i tako gotovo svaki povezani uređaj na zemlji drži svoj sat ispravnim.

U MicroPythonu to je jedna linija:

ntptime.settime()

ntptime je ugrađen u firmware, a settime() kontaktira vremenski server, čeka odgovor i iz njega postavlja unutarnji sat pločice. Za razliku od Arduino configTime(), koji pokreće zahtjev i vraća se odmah, settime() se ne vraća dok ne dobije odgovor ili dok ne odustane, pa kada se uspješno vrati, sat je već ispravan.

ℹ️
Adresu servera ne predaješ. ntptime ima jednu ugrađenu, i pokazuje na isti javni skup vremenskih servera koje Arduino skica izričito imenuje kao pool.ntp.org. Ako ti ikada zatreba drugi, to je ntptime.host = "my.server" prije poziva settime().

Skripta ga omotava u vlastitu funkciju tako da neuspjeh ne može srušiti cijeli sat:

def sync_time():
try:
ntptime.settime()
return True
except Exception as e:
print("Failed to obtain time from NTP:", e)
return False
ℹ️
Mrežni poziv u MicroPythonu pri neuspjehu diže iznimku umjesto da vrati kod greške, pa try i except ovdje nisu neobavezni. Vraćanje True ili False to pretvara u nešto o čemu ostatak programa može jednostavno pitati.

Vremenske zone su tvoj posao

NTP server uvijek odgovara u UTC, svjetskom referentnom vremenu. Nema pojma gdje si, pa ne može znati koliko je sati za tebe. Pretvaranje UTC-a u lokalno vrijeme ostavljeno je dvama brojevima na vrhu skripte:

GMT_OFFSET_SEC = 0
DAYLIGHT_OFFSET_SEC = 0

Oba su u sekundama, pa je jedan sat 3600. Prvi je koliko je tvoja vremenska zona daleko od UTC-a. Drugi je dodatni sat za ljetno računanje vremena, ako ga tvoja zemlja koristi.

ℹ️
Hrvatska je zimi jedan sat ispred UTC-a a ljeti dva, pa je GMT_OFFSET_SEC = 3600 cijele godine a DAYLIGHT_OFFSET_SEC = 3600 od kraja ožujka do kraja listopada. Skripta dolazi s oba postavljena na 0, i zato ekran na fotografijama niže pokazuje 10:29 kada je sat na zidu pokazivao 12:29. Ako ti je sat pogrešan za cijeli broj sati, to je razlog. Nije ništa pokvareno.

Dobivanje sata i minute iz sata

MicroPython ti sat ne predaje direktno. time.time() ti daje brojanje sekundi, a time.localtime() takvo brojanje razdvaja u njegove dijelove:

def get_current_time():
local = time.localtime(time.time() + GMT_OFFSET_SEC + DAYLIGHT_OFFSET_SEC)
return local[3], local[4]

Ono što se vrati je tuple od osam vrijednosti, a one koje želiš odabireš po poziciji: pozicija 3 je sat a pozicija 4 je minuta. Dodavanje dvaju pomaka prije razdvajanja je ono što odgovor pomiče iz UTC-a u lokalno vrijeme.

ℹ️
Cijeli redoslijed je godina, mjesec, dan, sat, minuta, sekunda, dan tjedna, dan godine. Brojeći od nule, to sat stavlja na 3 a minutu na 4. return local[3], local[4] vraća oboje odjednom, i zato onaj koji poziva može napisati hour, minute = get_current_time() u jednoj liniji.

Dopunjavanje cifara

Sedam minuta nakon deset trebalo bi pisati 10:07, a ne 10:7. Python to radi specifikatorom formata:

lcd.print("Time: {:02d}:{:02d}".format(hour, minute))

{:02d} znači cijeli broj, širok najmanje dvije cifre, dopunjen nulom ako je kraći.

ℹ️
To je urednije od Arduino verzije istog projekta, kojoj treba sprintf() s %02d u jednom redu i ručno napisano "ispiši prvo 0 ako je broj manji od deset" u drugom. Ovdje oba reda koriste taj isti jedan specifikator.

Obilaženje na 24 i 60

Svaki pritisak tipke svoj broj nabroji gore, a oba se moraju prevrnuti:

alarm_hour = (alarm_hour + 1) % 24
alarm_minute = (alarm_minute + 1) % 60

% je operator ostatka. 24 % 24 je 0, pa sat ide 22, 23, 0, 1, točno ono što sat i radi. Isti trik sa 60 drži minute u rasponu.

Zašto alarmu treba pamćenje

Pločica prolazi kroz petlju oko deset puta u sekundi, a alarm se oglašava kada se sat poklopi s vremenom alarma. Ali minuta traje šezdeset sekundi, pa bi se obična usporedba poklopila oko šest stotina puta u nizu i buzzer nikada ne bi prestao.

To sprječavaju dvije varijable. alarm_triggered postavlja se onog trenutka kada se alarm oglasi i blokira svako daljnje oglašavanje dok ne pritisneš tipku. last_alarm_minute pamti u kojoj se minuti oglasio, tako da pritisak tipke za brisanje ekrana ne može odmah ponovno pokrenuti alarm dok ta ista minuta još traje. Počinje na -1, vrijednosti koju nijedna prava minuta ne može imati, pa prvi alarm nikada nije blokiran.

I zašto provjerava je li vrijeme stvarno

Postoji još jedna zaštita, i ona nema ekvivalenta u Arduino skici:

if not time_synced:
lcd.setCursor(0, 0)
lcd.print("Waiting for time")
# ...probaj sinkronizaciju ponovno...
continue

Dok sinkronizacija ne uspije, unutarnji sat pločice nije postavljen na ništa smisleno. Njegovo prikazivanje bilo bi zavaravajuće, a usporedba alarma s njim mogla bi alarm pokrenuti u slučajnom trenutku. Zato skripta na ekranu piše Waiting for time, sinkronizaciju pokušava jednom u minuti, a continue preskače ostatak petlje dok ne uspije.


Kod

Ovdje je cijela skripta. Upiši svoje ime mreže i lozinku u WIFI_SSID i WIFI_PASS prije nego je pokreneš, i pobrini se da je lib folder na pločici.

# I2C je ono što Qwiic konektor nosi, PWM pobuđuje buzzer, a Pin čita dvije tipke.
from machine import I2C, Pin, PWM

# Soldered driver za LCD ekran, koji se nalazi u lib folderu repozitorija primjera.
from LCD import LCD_I2C

# Modul network pridružuje se Wi-Fi mreži.
import network

# Modul ntptime pita NTP server na internetu koliko je sati i iz odgovora postavlja sat pločice. NTP je
# skraćeno od Network Time Protocol, i tako gotovo svaki uređaj na internetu drži svoj sat
# ispravnim.
import ntptime
import time

# Ove dvije varijable drže ime tvoje Wi-Fi mreže (SSID) i njezinu lozinku.
WIFI_SSID = "your ssid"
WIFI_PASS = "your password"

# NTP serveri uvijek odgovaraju u UTC-u, svjetskom referentnom vremenu, pa pločici moramo reći koliko je naša vremenska zona
# daleko od njega. Pomak se zadaje u sekundama, pa je jedan sat 3600. Hrvatska je zimi jedan sat ispred UTC-a,
# što bi ovdje bilo 3600.
# Druga vrijednost je dodatni pomak za ljetno računanje vremena. Postavi ga na 3600 tijekom ljetnog vremena u zemlji koja
# ga koristi, a inače ga ostavi na 0.
GMT_OFFSET_SEC = 0
DAYLIGHT_OFFSET_SEC = 0

# Ovo su varijable kojima predajemo brojeve pinova na koje smo spojili dvije TIPKE i buzzer.
# Jedna tipka nabraja sat alarma gore, druga nabraja minutu alarma gore.
BUTTON_HOUR = 2
BUTTON_MIN = 3
BUZZER_PIN = 4

# Ove varijable drže vrijeme alarma prema kojem brojimo, počevši od 07:00, i pamte je li se alarm
# već oglasio, tako da se oglasi jednom a ne iznova tijekom iste minute.
alarm_hour = 7
alarm_minute = 0
alarm_triggered = False

# Ova varijabla pamti u kojoj se minuti alarm posljednji put oglasio. Bez nje bi zaustavljanje alarma pritiskom tipke
# unutar same minute u kojoj se oglasio dopustilo da se oglasi ponovno odmah. Počinjemo na -1, vrijednosti koju nijedna prava minuta ne može
# imati, tako da prvi alarm nikada nije blokiran.
last_alarm_minute = -1

# Ova varijabla pamti jesmo li ikada uspjeli doći do vremenskog servera. Dok nismo, sat pločice
# nije postavljen na ništa smisleno, pa alarm ne smijemo usporediti s njim.
time_synced = False

# Ovo su varijable koje se koriste za debounce tipki, ista tehnika objašnjena u primjeru 2.2. Budući da ovdje imamo
# dvije tipke, svakoj treba vlastiti par varijabli.
last_hour_state = 1
last_min_state = 1
last_hour_change_ms = 0
last_min_change_ms = 0
DEBOUNCE_MS = 25

# Ove dvije varijable upravljaju koliko često ponovno pitamo NTP server koliko je sati. Sat pločice polako odlutava,
# pa provjeravanje s vremena na vrijeme drži njegovu točnost.
last_sync = 0
SYNC_INTERVAL_MS = 60000

# Ovdje postavljamo I2C vezu i stvaramo naš objekt ekrana. Na NULA MINI, I2C koristi IO6 za liniju podataka
# (SDA) i IO7 za liniju takta (SCL), a to su pinovi na koje je Qwiic konektor spojen.
i2c = I2C(0, scl=Pin(7), sda=Pin(6))
lcd = LCD_I2C(i2c)

# Ovdje stvaramo naše Pin objekte za dvije tipke. Pin.PULL_UP uključuje otpornik unutar čipa koji veže
# svaki pin na 3.3V dok je njegova tipka otpuštena, pa pin čita visoko (1) kada mu je tipka gore i nisko (0) dok je
# pritisnuta.
hour_btn = Pin(BUTTON_HOUR, Pin.IN, Pin.PULL_UP)
min_btn = Pin(BUTTON_MIN, Pin.IN, Pin.PULL_UP)

# Ovdje stvaramo naš PWM objekt za buzzer. duty_u16() postavlja koji dio vremena pin ostaje uključen, a polovica
# cijelog raspona buzzeru daje njegov najčišći ton.
buzzer = PWM(Pin(BUZZER_PIN))
SOUND_ON = 32768
SOUND_OFF = 0
buzzer.duty_u16(SOUND_OFF)


def get_current_time():
# Ovo je funkcija koju smo napisali sami. Vraća trenutni sat i minutu u našoj vremenskoj zoni.
# time.time() nam daje vrijeme koje pločica zna, brojano u sekundama, a dodavanje naših dvaju pomaka to pomiče
# iz UTC-a u lokalno vrijeme. time.localtime() zatim rezultat razdvaja u odvojene dijelove, od kojih je pozicija 3
# sat a pozicija 4 minuta.
local = time.localtime(time.time() + GMT_OFFSET_SEC + DAYLIGHT_OFFSET_SEC)
return local[3], local[4]


def sync_time():
# Ova funkcija pita NTP server koliko je sati i iz odgovora postavlja sat pločice.
# Omotavamo je u try blok jer sve što ide preko mreže može pasti, a neuspjela sinkronizacija ne bi trebala zaustaviti
# cijeli sat.
try:
ntptime.settime()
return True
except Exception as e:
print("Failed to obtain time from NTP:", e)
return False


def beep_alarm():
# Ova funkcija oglašava alarm. freq() postavlja visinu tona, u ovom slučaju 1000 Hz, a uključivanje i isključivanje zvuka
# pet puta u nizu je ono što jedan dugi ton pretvara u niz pisaka.
buzzer.freq(1000)
for _ in range(5):
buzzer.duty_u16(SOUND_ON)
time.sleep_ms(300)
buzzer.duty_u16(SOUND_OFF)
time.sleep_ms(100)


# Ovdje pripremamo ekran. begin() pokreće komunikaciju i mora doći prvi. backlight() zatim pali
# svjetlo iza ekrana, i mora doći nakon begin(), koji bi ga inače ponovno ugasio.
lcd.begin()
lcd.backlight()

# clear() briše sve što je ostalo na ekranu od prije, a setCursor() odabire gdje se sljedeći tekst pojavljuje:
# prvi broj je stupac a drugi je red, oba brojeći od nule.
lcd.clear()
lcd.setCursor(0, 0)
lcd.print("Connecting WiFi")

# Ovdje stvaramo naš mrežni objekt, uključujemo Wi-Fi hardver i pokrećemo pokušaj povezivanja. connect()
# ne čeka da se povezivanje dovrši, pa na njega čekamo sami u petlji ispod.
wlan = network.WLAN(network.STA_IF)
wlan.active(True)
wlan.connect(WIFI_SSID, WIFI_PASS)
while not wlan.isconnected():
time.sleep(0.5)
print(".", end="")

print()
print("WiFi connected!")
lcd.clear()
lcd.print("WiFi connected")
time.sleep(1)

# Sada kada smo na mreži možemo pitati koliko je sati. settime() čeka da odgovor dođe, pa kada se
# uspješno vrati, sat pločice je već ispravan.
if sync_time():
time_synced = True
last_sync = time.ticks_ms()
lcd.clear()
lcd.print("Time synced!")
else:
lcd.clear()
lcd.print("Sync failed!")
time.sleep(1)
lcd.clear()

while True:

# Ako nikada nismo došli do vremenskog servera, sat pločice nije postavljen na ništa smisleno. Njegovo prikazivanje bilo bi
# zavaravajuće a usporedba alarma s njim mogla bi alarm pokrenuti u pogrešnom trenutku, pa kažemo što se
# događa i sinkronizaciju pokušavamo ponovno u sljedećem prolazu.
# continue skače pravo natrag na vrh petlje.
if not time_synced:
lcd.setCursor(0, 0)
lcd.print("Waiting for time")
if time.ticks_diff(time.ticks_ms(), last_sync) > SYNC_INTERVAL_MS:
if sync_time():
time_synced = True
lcd.clear()
last_sync = time.ticks_ms()
time.sleep_ms(100)
continue

# Ovdje trenutno vrijeme čitamo preko naše funkcije iznad.
hour, minute = get_current_time()

# Ovdje gradimo tekst za ekran. "{:02d}" znači "cijeli broj napisan s najmanje dvije cifre,
# dopunjen nulom ako treba", a to je ono što 7 minuta nakon punog sata pretvara u "07" umjesto u "7".
lcd.setCursor(0, 0)
lcd.print("Time: {:02d}:{:02d}".format(hour, minute))

# U drugoj liniji prikazujemo vrijeme alarma, dopunjeno na isti način.
lcd.setCursor(0, 1)
lcd.print("Alarm {:02d}:{:02d}".format(alarm_hour, alarm_minute))

# Ovo je logika debouncea za tipku sati, ista ona objašnjena u primjeru 2.2.
# Budući da tipke otpuštene čitaju visoko, pritisak je trenutak kada vrijednost ide s 1 na 0, a to je
# točno trenutak kada sat nabrajamo gore. Nakon 23 počinjemo iznova od 0, jer sata 24 nema.
hour_reading = hour_btn.value()
now = time.ticks_ms()
if hour_reading != last_hour_state and time.ticks_diff(now, last_hour_change_ms) > DEBOUNCE_MS:
last_hour_change_ms = now
if last_hour_state == 1 and hour_reading == 0:
alarm_hour = (alarm_hour + 1) % 24
last_hour_state = hour_reading

# Ta ista logika za tipku minuta, nabrajajući do 59 prije nego počne iznova.
min_reading = min_btn.value()
if min_reading != last_min_state and time.ticks_diff(now, last_min_change_ms) > DEBOUNCE_MS:
last_min_change_ms = now
if last_min_state == 1 and min_reading == 0:
alarm_minute = (alarm_minute + 1) % 60
last_min_state = min_reading

# Ovdje provjeravamo je li vrijeme za alarm. Svi uvjeti moraju biti istiniti odjednom: sat se mora
# poklopiti, minuta se mora poklopiti, i alarm se ne smije već oglasiti.
if hour == alarm_hour and minute == alarm_minute and not alarm_triggered and minute != last_alarm_minute:
alarm_triggered = True
last_alarm_minute = minute
lcd.clear()
lcd.setCursor(0, 0)
lcd.print("ALARM!")
print("Alarm Triggered!")
beep_alarm()

# Pritisak bilo koje tipke nakon što se alarm oglasio briše upozorenje s ekrana i ponovno naoružava alarm
# za sljedeći dan.
if alarm_triggered and (hour_reading == 0 or min_reading == 0):
alarm_triggered = False
lcd.clear()

# A ovdje s vremena na vrijeme ponovno pitamo NTP server koliko je sati, tako da sat ne odluta.
if time.ticks_diff(time.ticks_ms(), last_sync) > SYNC_INTERVAL_MS:
sync_time()
last_sync = time.ticks_ms()

# Kratka pauza da se ekran ne prepisuje tisućama puta u sekundi. Držanje kratke također drži
# tipke odzivnima.
time.sleep_ms(100)

Što bi trebao vidjeti

Pokretanje

Skripta svoje pokretanje pripovijeda na ekranu, jedan ekran po ekran. Prvo najavljuje da se pridružuje mreži:

LCD prikazuje Connecting WiFi
Pločica pokušava pristupiti tvojoj mreži. Ovaj ekran stoji dok ne uspije
⚠️
Ovaj ekran nema vremensko ograničenje. Petlja čekanja nema istek vremena uopće, pa ako je ime mreže ili lozinka pogrešna, pločica ovdje sjedi zauvijek a da ti to nikada ne kaže. Ako se nakon desetak sekundi nije pomaknula, provjeri WIFI_SSID i WIFI_PASS za tipfelere, a ne spajanje. Točke u konzoli jedini su znak da još pokušava.

Kada je na mreži, dobiješ sekundu potvrde:

LCD prikazuje WiFi connected
Povezano. Ovaj ekran traje jednu sekundu

Zatim pločica pita vremenski server koliko je sati. Budući da ntptime.settime() čeka svoj odgovor, ovdje nema zasebnog ekrana "sinkroniziram". Sljedeća stvar koju vidiš je rezultat. Ako odgovor dođe, dobiješ Time synced! na sekundu i sat počinje. Ako ne, umjesto toga se pojavi Sync failed!, a petlja propada do Waiting for time, pokušavajući jednom u minuti dok ne uspije.

ℹ️
To je mala razlika u odnosu na Arduino verziju ovog projekta, koja prikazuje ekran Syncing time... dok propituje za odgovor koji dolazi u pozadini. Ovdje se čekanje odvija unutar jednog blokirajućeg poziva, pa se tijekom njega nema što prikazati.

U konzoli

Resetiraj pločicu i cijelo pokretanje ulazi u nekoliko linija:

......
WiFi connected!

Svaka točka je jedan krug petlje čekanja, a svaki krug traje 500 ms, pa je red točaka štoperica. Šest točaka ovdje znači oko tri sekunde do pridruživanja mreži. Očekuj negdje između četiri i osam; točan broj mijenja se od pokretanja do pokretanja, jer ovisi o tome kako brzo tvoj router odgovara. Ako ikada budeš htio znati je li tvoja mreža spora u dodjeljivanju adresa, taj red točaka je mjerenje.

ℹ️
Ako sinkronizacija ne uspije, dobiješ i Failed to obtain time from NTP: nakon čega slijedi razlog, ispisan jednom u minuti dokle god ne uspijeva. Neke gostinske i korporativne mreže vremenske servere blokiraju u cijelosti.

Sat u radu

Nakon što se ekrani pokretanja očiste, ekran se smjesti u dvije linije koje će odsad prikazivati: trenutno vrijeme gore, vrijeme alarma ispod:

LCD prikazuje Time: 10:29 u gornjem redu i Alarm 10:29 u donjem redu
Sat u radu, s alarmom postavljenim na minutu koja upravo dolazi

Oba su dopunjena na dvije cifre, pa sedam minuta nakon punog sata pokazuje 07 a ne 7.

ℹ️
Zapamti vremensku zonu. Fotografija pokazuje 10:29 jer skripta dolazi s GMT_OFFSET_SEC = 0 i zato prikazuje UTC. U prostoriji je bilo 12:29. Postavi pomak i gornji red postaje tvoje lokalno vrijeme.

Postavljanje i slušanje alarma

Alarm počinje na 07:00. Pritisni tipku na IO2 da nabrajaš sate gore i onu na IO3 da nabrajaš minute, i gledaj kako se donji red mijenja. Oba obilaze: 23 ide natrag na 0, a 59 natrag na 0.

Da alarm čuješ bez cjelodnevnog čekanja, postavi ga na minutu koja upravo dolazi. Kada ga sat sustigne, gornji red se zamjenjuje:

LCD prikazuje ALARM! dok se buzzer oglašava
Alarm se oglašava: pet pisaka, i ALARM! na ekranu
ℹ️
Taj ekran je gore samo oko dvije sekunde. beep_alarm() svira pet pisaka od 300 ms sa 100 ms tišine između njih, a ekran se ne dira ponovno dok ne završi. Onog trenutka kada piskanje prestane, petlja se izvrši još jednom i preko ALARM! upisuje vrijeme. Ako to želiš fotografirati, imaš jedan pokušaj kada piskanje počne.

Konzola u istom trenutku ispisuje Alarm Triggered!, što je korisno ako si propustio ekran.

⚠️
Alarm pisne jednom i sam prestane. Ne nastavlja se oglašavati dok ne pritisneš tipku. Pet pisaka i gotovo je. Pritisak tipke nakon toga briše ALARM! s ekrana i ponovno naoružava alarm za sljedeći put.
ℹ️

Postoji jedna osobitost tog pritiska tipke koju vrijedi znati. Skripta tipke čita prije nego provjeri treba li obrisati alarm, pa isti onaj pritisak koji briše ekran također nabraja vrijeme alarma gore za jedan. Utišaj ga tipkom sati i alarm se pomiče s 10:29 na 11:29.

Koju tipku koristiš važnije je nego što izgleda. Tipka minuta alarm potiskuje na sljedeću minutu, pa se ponovno oglasi šezdeset sekundi kasnije. Utišaj i taj tipkom minuta i on se ponovno pomiče, i imaš alarm koji se oglašava svake minute dokle god mu tako odgovaraš. Koristi tipku sati da ga utišaš, što ti daje sat vremena, ili resetiraj pločicu.

To je skripta koja radi kako je napisana, a ne greška, i vrijedi to razumjeti prije nego to pokušaš popraviti: obrada tipki i provjera alarma dva su odvojena bloka koji oba gledaju isti pritisak.

Ako nešto nije u redu

Ekran je upaljen ali prazan. To ima tri različita uzroka, a konzola ih razlikuje u nekoliko sekundi, pa pogledaj tamo prije nego diraš spajanje.

  • Ničega uopće, i skripta se zaustavi s OSError: [Errno 19] ENODEV. Pločica ne dolazi do ekrana. Provjeri scl=Pin(7), sda=Pin(6) u liniji I2C(), pa je li Qwiic kabel propisno na mjestu na oba kraja.
  • Konzola pokazuje točke i WiFi connected!, ali ekran ostaje taman. Pločica razgovara s ekranom a ekran je jednostavno previše blijed da bi se čitao. Okreni CONTRAST trimpot na ljubičastoj adapterskoj pločici. Vidi 4.1.
  • Konzola ponavlja Failed to obtain time from NTP. Pločica je na mreži ali ne može dobiti vrijeme, pa petlja nikada ne prođe dalje od Waiting for time.
ℹ️
Prvi slučaj vrijedi razmotriti. Upaljeno pozadinsko svjetlo nije dokaz ničega: njegovo uključivanje je i samo poruka poslana preko Qwiic kabela, ali izlazi ekspandera pri uključivanju sjede visoko, pa se svjetlo upali bez obzira je li ta poruka ikada došla. To je ista zamka kao u koraku 11.

Sat je pogrešan za cijeli broj sati. To je pomak vremenske zone, a ne greška. Vidi iznad.

Jedna tipka ne radi ništa. Provjeri koristi li jednu nožicu s obje strane središnjeg kanala. Tipka spojena s obje žice na istoj strani čita se kao trajno pritisnuta, a ona s žicom mase u pogrešnom redu ne radi ništa uopće.

Buzzer je tih ali se ALARM! pojavljuje. Kod je u redu a greška je u posljednje dvije žice. Provjeri da su nožice buzzera stvarno u dva različita reda, i da je IO4 pin u j27. S druge strane pločice, a27 je IO19.

ImportError: no module named 'LCD'. lib folder nije na pločici. Vidi Postavljanje MicroPythona.


Cijeli primjer

Pogledaj cijeli kod primjera na linku ispod:

7.7_Alarm_Clock.py

Budilica povezana na Wi-Fi: pločica vrijeme dobiva s NTP servera, prikazuje ga na Qwiic LCD-u i oglašava buzzer u vrijeme alarma postavljeno dvjema tipkama.