V preteklem smo na vremensko postajo 3.0 priključiti še tipali za merjenje relativne zračne vlage in zračnega pritiska, poleg teh pa izdelati še zunanje merilne enote, ki jih bodo poganjali ESP32 moduli. Preverili smo tudi možnosti za priklop precej zmogljivejšega 4-inčnega Waveshareovega prikazovalnika z zaslonom na dotik.
Avtor besedila in fotografij: dr. Simon Vavpotič
E-pošta:simon.vavpotic@gmail.com
2022-305-40
Tokrat bomo vremenski postaji dodali tudi funkcionalnosti, ki jih potrebuje za predvajanje glasbe in uro budilko ter zaznavanje oseb v prostoru. Slednje omogoča samodejno vklapljanje in izklapljanje osvetlitve zaslona. Veliko se bomo tudi naučili iz primera spletnega radija, ki poleg predvajanja podatkovnih tokov spletnih radijev omogoča tudi prevajanje zvokovnih datotek.
Ta vsebina je samo za naročnike
Omenimo še, da smo Adafruitovo VS1053 (oziroma združljivo) razvojno ploščo že uporabili v preteklem nadaljevanju za priklop SD kartice, na kateri periodično shranjujemo izmerjene vremenske podatke. Za ESP32 bomo tokrat zato pripravili novo programsko o premo, ki bo vključevala tudi funkcije VS1053 čipa za snemanje, enostavne obdelave in predvajanje Hi-Fi zvoka iz MP3, OGG, FLAC in WAV datotek. Mikavna tudi možnost glasovnega upravljanja vremenske postaje, za katero bi morali še nekoliko dopolniti vgrajeno programsko opremo.
Kako namestiti priključek za linijski izhod?
Dokler se nisem lotil predvajanja zvokovnih datotek, je bil izhod VS1053 lahko skrit v ohišju vremenske postaje in obrnjen navzdol. Če bi ga hotel neposredno uporabiti, bi moral vremensko postajo položiti na hrbet, to pa bi bilo zelo nepraktično. K sreči je mogoče dodati zunanjo avdio vtičnico, njen avdio kabel pa z zgornje strani udobno prispajkati na sicer neuporabljeno vtičnico, kar je bistveno enostavneje kot kakršnakoli predelava elektronike vremenske postaje.
Morda ni odveč omeniti še način pritrditve zunanje vtičnice na zadnji del plastičnega ohišja. Slednje je predebelo, da bi lahko vtičnico privili z matico, vendar pa si lahko pomagamo tako, da izvrtino z zgornje strani povrtamo in s tem posredno stanjšamo plastiko. Tako matico brez težav privijemo, poglobljena vtičnica pa hkrati deluje veliko bolj profesionalno.
Adafruitova VS1053 programska knjižnica
Podpira vse funkcije zvokovnega modula v Arduino razvojnem okolju: nastavitev glasnosti za levi in desni kanal, predvajanje zvoka in snemanje zvoka. Za tekoče predvajanje ali snemanje zvoka mora nadzorni mikrokontroler (AVR, ESP, PIC, STM) z dovoljšnjo hitrostjo prenašati podatke med VS1053 in svojim pomnilnikom ter perifernimi enotami za masovno hrambo podatkov (najpogosteje mikro SD kartico). V pomoč so v Adafruitovi programski knjižnici tudi funkcije, ki podpirajo predvajanje zvokovnih datotek z različnimi nadzornimi mikrokontrolerji. Predvajanje zvoka je tako trivialno z enim klicem funkcije, v katerem podamo pot do datoteke na (mikro) SD kartici, a le dokler si lahko privoščimo predvajanje zvokovne datoteke v celoti. V nasprotnem primeru lahko uporabimo funkcijo za začetek predvajanja, nato pa poskrbimo, da nadzorni mikrokontroler sproti prenaša bloke podatkov v ali iz VS1053 modula.
Omenimo še, da lahko VS1053 modul tudi programiramo, oziroma v njegov RAM naložimo dodaten kodek, kar je še posebej pomembno pri tekočem snemanju zvoka z različnimi ločljivostmi. K sreči so na proizvajalčevi spletni strani na voljo tudi gotovi kodeku v obliki izvedljivih binarnih datotek, ki jih pred snemanjem s posebnim ukazom v Adafruitovi programski knjižnici preprosto prekopiramo v RAM VS1053 modula in nato z drugim ukazom zaženemo. Šele nato lahko zaženemo tudi snemanje zvoka. Kakorkoli, pri gradnji ure budilke nas zaenkrat bolj zanima predvajanje zvoka, ki ga, kot bomo videli v nadaljevanju, lahko zagotovimo na več načinov.
Kako zagotoviti nemoteno predvajanje Hi-Fi zvoka?
Čeprav lahko ESP32 modul deluje pri interni frekvenci 240 MHz, z zunanjimi napravami še zdaleč ne more tako hitro komunicirati. Zato je dejanska hitrost izvajanja programa odvisna tudi od zakasnitev pri komunikaciji z zunanjimi napravami. Denimo, zakasnitev pri komunikaciji s prikazovalnikom lahko pomeni težave pri predvajanju zvokov, če prenosov podatkov prikazovalnik ne razdelimo na manjše kose, med katerimi poteka prenos podatkovnih blokov z zvočnimi posnetki. Zakasnitve povzročajo tudi latence brezžičnih modulov in senzorjev temperature, tlaka in relativne vlažnosti zraka. Zato je potrebno vse podatkovne prenose prilagoditi časovno kritičnim podatkovnim prenosom.
Vsako zunanjo napravo lahko z ESP32 modulom povežemo s svojim vodilom, lahko pa namesto tega uporabimo eno vodilo za več naprav in med njimi preklapljamo. Slednje zahteva tudi manj priključkov na ohišju čipa. Denimo, Adafruitova razvojna plošča združuje priključke SCK, MOSI in MISO VS1053 čipa, kakor tudi ležišča SD kartice. Če ležišče uporabljamo za SD kartico, je lahko hkrati aktivna samo ena od obeh naprav. SD kartico lahko prek lastnega SPI vodila povežemo le, če vgradimo dodatno ležišče za mikro SD ali SD kartico. Zakaj je to pomembno? VS1053 modul ima vgrajen sorazmerno majhen izravnalnik, vendar nima neposrednega dostopa do SD kartice. Pri predvajanju ali snemanju zvoka mora za prenos podatkov iz SD kartice v izravnalnik VS1053 in obratno poskrbeti ESP32 modul, oziroma nadzorni mikrokontroler.
Po drugi strani, delovanje ESP32 modula zavirajo sorazmerno počasne periferne naprave. Velik pretok podatkov zahteva tudi hitro osveževanje vsebine barvnega prikazovalnika z SSD1351 kontrolerjem, ki zmore hitrosti SPI vodila do okoli 4,5 MHz (testiral sem tudi z 10 MHz in je delovalo). Vendar prenosa podatkov med ESP32 in SSD1351 ne moremo venomer povečevati, lahko pa uporabimo programerski trik in v programski kodi glavne zanke večkrat izvedemo naslednji pogojni stavek:
if(musicPlayer.playingMusic)
{musicPlayer.feedBuffer();}
s katerim med predvajanjem glasbe po potrebi oddamo nov podatkovni blok z digitalnim zvokom. To je podobno kot če uporabimo prekinitev s časovnikom, le da nam ni potrebno paziti na usklajevanje dostopa do SD kartice in drugih perifernih naprav, saj stavek izvedemo iz glavne programske zanke. Povejmo še, da moramo prej zgraditi programski objekt musicPlayer, kar storimo z ukazom:
Adafruit_VS1053_FilePlayer musicPlayer =
Adafruit_VS1053_FilePlayer(RESET, CS, DCS,
DREQ, CARDCS);
v katerem navedemo tudi vezavo priključkov VS1053 modula na nadzorni mikrokontroler (določimo vrednosti konstant: RESET, CS, DCS, DREQ in CARDS):
Druga možnost v Adafruitovi programski knjižnici je uporaba prekinitve na GPIO, na katerega vežemo signal DREQ, ki ga VS1053 postavi na logično vrednost 1, ko potrebuje nov podatkovni blok. Delovanje prekinitve vzpostavimo s preprostim ukazom:
musicPlayer.useInterrupt(VS1053_
FILEPLAYER_PIN_INT);
Pri tem nam ni potrebno skrbeti za pravo povezavo prekinitve in GPIO, na katerega smo vezali DREQ signal, saj je za to poskrbljeno že znotraj Adafruitove programske knjižnice. Omenjena funkcija vrne vrednost true, če vzpostavitev prekinitve uspe, in vrednost false v nasprotnem primeru.
Povejmo še, da lahko delovanje prekinitve vzpostavimo tudi mimo Adafruitove programske knjižnice, z ukazom attachInterrupt in pripadajočim prekinitvenim programom, ki ga moramo v tem primeru napisati sami. Je pa tudi res, da podpora za prekinitveni način delovanja v Adafruitovi programski knjižnici ni najbolje izvedena v primerih, ko SD kartice ne uporabljamo zgolj za delo z VS1053 modulom, ampak do nje dostopamo tudi iz drugih delov programske kode glavnega programa. Posledica je sesutje Arduinovega jedra. Za implementacijo je tako še najlažja prva rešitev, lahko pa se namesto tega zgledujemo po primeru spletnega z ESP32, objavljenem na spletni strani PC USB Projects (sites.google.com/site/pcusbprojects).
Glasnost in barva zvoka
Adafruitova programska knjižnica ima funkcijo za določanje slabljenja zvoka, posebej za levi in desni stereo kanal. Največjo glasnost tako predstavlja vrednost 0, največje slabljenje pa vrednost 255. Seveda lahko potrebno slabljenje signala izračunamo tudi iz nastavitve glasnosti, tako da je slabljenje = 255 – glasnost. Slabljenje nato nastavimo za oba kanala s funkcijo:
musicPlayer.setVolume(255-music_volume);
kjer je music_volume glasnost. Pri preizkušanju z 32-ohmskimi slušalkami, je so bile smiselne nastavitve glasnosti od 150 naprej, ostalo pa je bilo pretiho za normalno poslušanje.
Čisto druga zgodba pa je z nastavitvijo barve zvoka. Moja različica Adafruitiove programske knjižnice z decembra lani ni imel vgrajene ustrezne funkcije, a kljub temu sem med konstantami v datoteki glave (Adafruit_VS1053.h)našel tudi vrstico:
#define VS1053_REG_BASS 0x02
//!< Built-in bass/treble control
Zato je bilo več kot očitno, da VS1053 podpira nastavitve nizkih kot visokih tonov, kar zasledimo tudi v njegovi podatkovni poli med opisi polj posameznih 16-bitnih krmilnih registrov. Prednastavljena vrednost registra BASS je 0, kar pomeni nespremenjen zvok. Zato sem v izvorno kodo Adafruitove programske knjižnice dodal še naslednjo funkcijo:
void Adafruit_VS1053::setBassTreble(uint8_t vbass, uint8_t flbass,uint8_t vtreble,
uint8_t fltreble ) {
uint16_t v;vtreble&=0xf;fltreble&=0xf;
vbass&=0xf;flbass&=0xf;
v = vtreble; // in 1.5 dB steps
v <<= 4;v|=fltreble; // freq limit in 1000 Hz steps
v <<= 4;v|=vbass; // in 1.5 dB steps
v <<= 4;v|=flbass; // freq limit in 10 Hz steps
noInterrupts();sciWrite(VS1053_REG_BASS, v);interrupts();
}
ki nastavi barvo zvoka za nizke tone in visoke tone, pri čemer lahko izbiramo tudi spodnji frekvenčni omejitvi ojačenja nizkih in visokih tonov, pri čemer omejitev za nizke tone nastavljamo v korakih po 10 Hz od 0 Hz naprej, omejitev za visoke tone pa po korakih po 1000 Hz od 0 Hz naprej. Jakost ojačenja nizkih tonov nastavljamo od 0 dB naprej po korakih 1 dB (vrednosti 1..15, 0 izklop), medtem ko jakost ojačenja visokih tonov nastavljamo od -8 do 7 dB s korakom 1,5 dB (vrednosti 1 .. 15, 0 izklop). Ob napisanem ste verjetno že uganili, da vsakega od parametrov nastavljamo s 4-bitno vrednostjo, pri čemer vrednost jakosti 0 pomeni izklop funkcionalnosti. Če si nekoliko podrobneje ogledamo še podprograme za snemanje zvoka iz linijskega vhoda ali mikrofona (vgrajen je v večino razvojnih plošč z VS1053 čipom), ugotovimo, se pred snemanjem barva zvoka izklopi, saj podprogram, ki sproži snemanje, v register BASS zapiše vrednost 0. Če slednje izpustimo, lahko snemamo tudi predprocesiran zvok z nastavljeno barvo.
Ura budilka
Vremenska postaja prikazuje uro, zdaj moramo dodati le še alarme, od katerih lahko vsakemu priredimo drug zvok iz strnjenih glasbenih datotek. Nastavitve v programski kodi bomo zaenkrat izvedli iz aplikacije na PCju prek RS232 vmesnika. Za osnovo bomo uporabili projekt preklopne ure, ki omogoča programiranje več vklopov in izklopov releja. Programsko kodo bomo razširili tako, da bomo izklope izpustili, obenem pa omogočili, izbiro zvokovne datoteke, ki jo bi predvajal VS1053 modul ob sprožitvi alarma.
Osnova za proženje alarmov je urnik, ki lahko prestavimo z naslednjo podatkovno strukturo:
struct schedule_item {
int recurring;
int t_year;int t_month;int t_day;int t_hour;int t_min;int t_sec;
int action; String s_param;
};
Urnik sestavljen iz postavk, ki vsebujejo ponovljivost dogodka, ki je lahko: 0 – enkraten dogodek, 1 – ponovitev 1x na dan, 2 – ponovitev 1x na teden, 3 – ponovitev 1x na mesec, 4 – ponovitev 1x na leto, čas dogodka (leto, mesec, dan, ura, minuta, sekunda), akcija, ki se izvede (denimo predvajanje zvoka) in parameter akcije (denimo pot do zvokovne datoteke).
Potrebujemo tudi procesno logiko za obdelavo postavk, ki jo sestavljajo podprogrami za procesiranje urnika, dodajanje postavk, brisanje postavk ter vzpostavitev začetnega stanja oziroma zagon urnika. Poseben podprogram omogoča tudi izpis in pregledovanje urnika.
Seveda pa vse našteto še ni dovolj, saj potrebujemo tudi uporabniški vmesnik za urejanje alarmov. Še najenostavneje je, če jih nastavimo iz PCja prek terminalskega dostopa. Mogoče je tudi upravljanje prek OLED prikazovalnika in enkoderja s tipko, a prej moramo zgraditi kompleksnejši uporabniški vmesnik.
Zaznavanje gibanja v prostoru in samodejni vklop zaslona
Gotovo ni smiselno, da je OLED prikazovalnik vremenske postaje ves čas vklopljen, tudi ko nas ni v prostoru, saj se s tem veliko bolj obrablja. OLED prikazovalniki slabše kakovosti pri intenzivni uporabi dobijo spomin, oziroma se besedilo ali slike na mestih, ki so vedno vklopljena nekako »vžge« v zaslon, tako da je ob zamenjavi vsebine še vedno delno vidno. Nekaj podobnega smo bili vajeni v osemdesetih letih preteklega stoletja, ko smo uporabljali peceje z monokromatskimi zasloni in grafične kartice Hercules…
AM312 PIR senzor je ena najcenejših rešitev za zaznavanje gibanja v prostoru s pomočjo infrardeče svetlobe. V miniaturnem razvojnem vezju, za katerega na nemškem Amazonu odštejemo okoli štiri evre, ima tudi belo ohišje za lepšo razporeditev svetlobe ter 3,3-voltni napetostni stabilizator v čipu z oznako 7533, zato ga lahko uporabljamo tudi s 5-voltnimi mikrokontrolerji. Razvojna plošča z AM312 senzorjem ima tri priključke, po enega za napajanje in maso ter signalni izhod, katerega izhodna napetost je med 0 in 3,3 V. Visok napetostni nivo pomeni zaznavo gibanja, nizek pa odsotnost gibanja. Kljub temu bi bilo v vgrajeni programski kodi mikrokontrolerja potratno venomer preverjati stanje PIR senzorja, zato si lahko pomagamo z GPIO, s katerim povežemo strojno prekinitev. Ob zaznavi gibanja, se tako veno sproži prekinitveni program, ki za nekaj časa vklopi prikazovalnik, nato pa se ta ponovno izklopi, ko ni več gibanja.
Za implementacijo vklopa in izklopa OLED prikazovalnika s krmilnikom SSD1351 lahko v glavnem programu uporabimo kar funkcijo oled.Write_Command, če smo prej v glavnem programu zaslon definirali kot objekt oled. Funkcija krmilniku posreduje poljuben ukaz, pri čemer kode ukazov najdemo v glavi programske knjižnice v datoteki OLED_Driver.h (moja različica knjižnice, ki podpira 262.000 barv ima oznako OLED_Driver18.h, saj je barva vsake točke zaslona podana z 18 biti namesto s 16, kot pri originalnem gonilniku). Kakorkoli, prikazovalnik izklopimo z ukazom oled.Write_Command(SSD1351_CMD_DISPLAYOFF), ko PIR senzor dlje časa ne zazna premikov, kar pomeni, da ni v prostoru nikogar, prižgemo pa takoj, ko PIR senzor zazna gibanje. Tako je vklop zaslona samodejen in so vremenski podatki na voljo vedno, ko jih morebiti potrebujemo.
Poglejmo še, kako je izdelan prekinitveni podprogram za prižiganje zaslona:
void IRAM_ATTR PIR_isr() {
noInterrupts();
Serial.print(“.”);
if(disp_screen_saver_active){
disp_start_ts=millis();
Serial.println(“PIR = on”);
if(!disp_status){
oled.Write_Command(SSD1351_CMD_DISPLAYON); // display on
disp_status=true;}
} interrupts();}
Poleg tega moramo v podrpogramu Setup v Arduino kodi zagnati tudi ukaz
attachInterrupt(PIR,PIR_isr,RISING);
s katerim aktiviramo prekinitev na PIR priključku, ko ta ob zaznavi premikanja spremeni logično vrednost iz nizke (0) v visoko (1). Ugašanje prikazovalnika je implementirano v sklopi glavne zanke (Loop)z naslednjim podprogramom:
if((disp_screen_saver_active)&&(!digitalRead(PIR))){
if(disp_status){
if((uint32_t)(millis()-disp_start_ts)>disp_screen_saver_ms){
oled.Write_Command(SSD1351_CMD_DISPLAYOFF); // display off
disp_status=false;
}}}
Opozorimo le še na to, da moramo biti pri izbiri priključkov GPIO previdni, saj morajo ti omogočati tudi namestitev strojne prekinitve, če jo želimo uporabiti. V nasprotnem, lahko stanje priključka preverjamo ciklično v glavni zanki, za kar nekoliko razširimo zgornji podprogram:
if(disp_screen_saver_active){
if(digitalRead(PIR)){
disp_start_ts=millis();
if(!disp_status){
oled.Write_Command(SSD1351_CMD_DISPLAYON); // turn display on
disp_status=true;}
} else if(disp_status){
if((uint32_t)(millis()-disp_start_ts)>disp_screen_saver_ms){
oled.Write_Command(SSD1351_CMD_DISPLAYOFF); // turn display off
disp_status=false;
}}}
Tako kot v zgornjih primerih tudi tu funkcionalnosti ohranjevalnika zaslona vklopimo s spremenljivko disp_screen_saver_active.
Rotacijski enkoder s tipko
Naprednejši uporabniški vmesnik je smiseln samo, če imamo tudi učinkovit način njegovega upravljanja. Izhode enkoderja s tipko (CLK, DT in SW) povežemo z ESP32 modulom povežemo prek treh vhodov. Od GPIO priključkov ESP32 modula se splača izbrati tiste, ki lahko generirajo strojne prekinitve, saj bomo v nasprotnem njihovo stanje morali preverjati s pomočjo prekinitvenega podprograma, proženega s časovnikom, ali pa v glavni zanki, kot vidimo zgoraj. Mimogrede opozorimo, da je enkoder pasivni element, ki izhode veže na maso, zato moramo zagotoviti upornosti proti napajanju, da dobimo napetosti za logični stanji 0 in 1.
Signala CLK in DT določata smer in hitrost vrtenja enkoderja, medtem ko je signal SW povezan z njegovo tipko. CLK se proži vsakokrat ko zavrtimo enkoder naprej ali nazaj, medtem ko sprememba vrednosti DT pove, v katero smer smo ga zavrteli. Pri izbiranju menijev in vnosu podatkov so premiki enkoderja pogosti, zato bi morali brez uporabe prekinitev njegovo stanje v glavni pogosto preverjati, da bi zagotovili ustrezno odzivnost uporabniškega vmesnika. Zato uporaba prekinitev skoraj nujna.
Poglejmo, kako pripravimo prekinitveni podprogram za enkoder. Izhoda CLK in DT vežemo na prekinitveni podprogram za merjenje pozitivnega in negativnega zasuka, pri čemer merimo število premikov v skladu z Machestersko notacijo, oziroma na osnovi vseh šestnajstih mogočih prehodov signalov DT in CT v novi stanji, ki jih lahko določimo tabelarično ali pa s programsko odločitveno logiko. Pri tem vsaki kombinaciji dvobitnega začetnega stanja in dvobitnega končnega stanja pripišemo vrednosti -1, 0 in 1, ki pomenijo obstanek na mesti, negativno ali pozitivno rotacijo.
Vnos podatkov v uporabniškem vmesniku
Lotimo se še izgradnje uporabniškega vmesnika, pri katerem je najpomembneje zagotoviti način za enostaven in intuitiven vnos predvsem številčnih podatkov in izbiro parametrov. Zato se najprej lotimo vnosa podatkov. Ena izmed možnosti je majhna navidezna tipkovnica s 25 črkami in 10 ciframi, ki jo lahko izrišemo v spodnji del prikazovalnika s 128 x 128 pikami. Po znakih tipkovnice se premikamo s pomočjo rotacije in pritiskanja na stikalo enkoderja. Navidezno tipkovnico lahko tudi poenostavimo, če jo potrebujemo samo za pisanje številk in jo izpišemo na manjšem prostoru tako, da vsebuje samo cifre desetiškega in opcijsko tudi 16-tiškega številčnega sestava.
Krmarjenje med vnosnimi polji je z enkoderjem sorazmerno enostavno. Zato pa je boljše vprašanje, kako jih izrisati. Pri tem si lahko pomagamo s funkcijami že programskih knjižnic OLED_Driver in OLED_GFX, ki omogočajo izris znakov. Pri tem lahko znak, ki ga še izbiramo vsakokrat posebej označimo. Pri ustvarjanju dinamične grafične podobe uporabniškega vmesnika si lahko pomagamo tudi z možnostmi obdelave samo dela zaslona, medtem ko ostane ostala vsebina nespremenjena. Pri tem lahko upoštevamo, da pri ponovnem izrisu znakovne vrstice enaki znaki prejšnjega besedila ostanejo nespremenjeni, če čezenj izpišem nekoliko spremenjeno besedilo, zato uporabnik dobi vtis, da je besedilo popravil samo na mestu urejanja.
Spreminjanje in prilagajanje nabora znakov
Pri enostavni računalniški grafiki si lahko učinkovito pomagamo tudi s spreminjanjem in prilagajanjem nabora znakov. A tu pogosto naletimo na ovire, saj so v C/C++ programski kodi slike znakov kodirane s številčnimi vrednostmi, ki jih ne moremo preprosto urejati in posodabljati. Na primer, nabor znakov, ki je definiran v datoteki ASCII_font.h v tabeli:
const unsigned char ascii_table_8x16[95]
[16]={….};
ima definiranih zgolj 95 znakov (ASCII kode: 32, … , 126). Tako ostane prostih še 161 ASCII kod, za katere lahko definiramo lastne znake, tudi take, s katerimi lažje izrisujemo grafične elemente, kar smo poznali tudi v tekstovni grafiki prvih PCjev.
Kako se lotiti dopolnjevanja nabora znakov? Vsak znak sestavlja 16 bajtov, ki so razvrščeni od najvišje ležečega do najnižje ležečega. Če hočemo dodati nov znak, lahko namesto šestnajstiškega zapisa v urejevalniku C/C++ kode uporabimo binarnega. Denimo, v znak »*« šestnajstiškem zapisu:
0x80,0xA0,0xE0,0xC0, 0xC0,0xE0,0xA0,0x80,
0x00,0x02,0x03,0x01, 0x01,0x03,0x02,0x00
prekodiramo tako, da vse bajte podpišemo in obenem spremenimo v binarni zapis, ki je berljiv prevajalniku za C/C++:
B10000000, // zgornja polovica
B10100000,
B11100000,
B10110000,
B10110000,
B11100000,
B10100000,
B10000000,
B00000000, // spodnja polovica
B00000010,
B00000011,
B00000001,
B00000001,
B00000011,
B00000010,
B00000000
Zdaj lažje opazimo, da je znak velikost 8×16 znakov izdelan tako, da je vsak znak po stolpcih razdeljen v zgornjo in spodnjo polovico. Biti so v posameznem stolpcu od vrha navzdol urejeni od najmanj pomembnega do najpomembnejšega. Spodnja polovica je za 8 pik nižje od zgornje zato bi si znak »*« veliko lažje predstavljali, če bi spodnjo in zgornjo polovico, vsako posebej, zasukali za 90 stopinj v levo in zgornjo polovico položili na spodnjo. Kakorkoli, poskusimo zvezdico spremeniti v znak za stopinjo (npr. st. C), oziroma krogec zgoraj:
B00000000, // zgornja polovica
B00000000,
B00000000,
B00111110,
B01100011,
B01100011,
B00111110,
B00000000,
B00000000, // spodnja polovica
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000,
B00000000
Čeprav lahko binarni zapis ohranimo, in vstavimo v C/C++ koda, je morda še enostavneje, če ga pred tem ponovno spremenimo v šestnajstiški zapis:
0x00,0x00,0x00,0x3E, 0x33,0x33,0x3E,0x00,
0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00
V zadnjem koraku moramo spodnjo vrstico le še dodati v tabelo znakov, kar je najlažje kot zadnjo vrstico na konec tabele. Tako je nova definicija tabele:
const unsigned char ascii_table_8x16[95][16]={….,
0x00,0xC0,0x60,0x30, 0x30,0x60,0xC0,0x00,
0x00,0x01,0x03,0x06, 0x06,0x03,0x01,0x00};
Zdaj moramo v programski datoteki OLED_GFX.cpp v funkciji Display_String_8x16 Adafruitove programske knjižnice OLED GFX le še spremeniti omejitev zadnje podprte ASCII kode znaka iz 0x7E (126) na 0x7F (127). Znak z ASCII kodo 127 bo tako krogec za stopinjo. Lahko pa namesto tega definiramo v datoteki ASCII_font.h definiramo konstanto LAST_ASCII_CODE_FONT_8x16, ki ji v datoteki glave priredimo vrednost 0x7F, saj tako pri nadaljnjem širjenju nabora znakov ne bo treba venomer brskati po izvorni kodi:
#define LAST_ASCII_CODE_FONT_8x16 0x7F
Konstanta bo vidna tudi v OLED_GFX.cpp,
saj ta vključuje ASCII_font.h.
Prihodnjič
Idej za dodajanje novih funkcionalnosti še kar ni dovolj. Prihodnjič nadaljujemo z gradnjo uporabniškega vmesnika in dodajanjem grafičnih elementov. Zanimalo nas bo tudi, kako vremenski postaji dodati še funkcionalnost internetnega radija. Ukvarjali se bomo tudi s hitrim prenosom podatkov, kot ga pozna večina naprav s priključki USB, denimo digitalni fotoaparat. Slednji se po priklopu na USB vtičnico navadno preklopi v način za polnjenje ali v način za prenos podatkov. Zanimal nas bo slednji, saj želimo imeti možnost hitrega kopiranja podatkov na kartici (mikro) SD, ne da bi jo odstranili iz naprave…