Programmering i Arduino
Ein mikrokontroller har eit innebygd kodespråk som forstår eit bestemt sett med kodar. Programmet ditt må bestå av kodar som samsvarer med dette kodesettet, og kodane må stå i rett rekkefølge. Då forstår mikrokontrolleren kva han skal gjere.
Når du lagar programkoden, kan du bruke eit programmeringsverktøy, eller du kan skrive i eit tekstbehandlingsprogram.
Fordelen med å bruke programmeringsverktøyet er at det gir ulik tekstfarge på ulike delar av programkoden, og det kan gjere koden meir oversiktleg. Programmeringsverktøyet har òg eit auto-formatverktøy som kan hjelpe deg å lage tekstinnrykk og formatere koden slik at han visuelt blir enklare å lese.
Når du skriv koden din i eit tekstbehandlingsprogram, må du hente han inn i programmeringsverktøyet for kompilering og lagring på Arduinoen. Same kva verktøy du har brukt, er det først ved kompilering at blir koden feilsjekka mot programmeringsspråket.
Eit Arduino-program består alltid av to funksjonar: setup() og loop().
Funksjonen setup() blir berre køyrd éin gong, og det er med ein gong brukar koplar til straumen.
Funksjonen loop() køyrer så lenge Arduinoen har straum. Her skriv du koden for det du vil Arduinoen skal gjere.
Kva kodeblokker som skal plasserast kvar, vil du forstå betre når du begynner å løyse oppgåver.
Når du skriv dobbel skråstrek (// ), vil ikkje programmet lese teksten som følger etter dette teiknet, som kode. Dobbel skråstrek bruker du derfor når du vil legge inn kommentarar i koden, anten for å beskrive for andre kva programmet skal gjere, eller heilt enkelt for å ha ein hugselapp til deg sjølv.
I Arduino-programmering og i mange andre programmeringsspråk bruker vi semikolon (;) som eit avslutningsteikn.
Vi deler vanlegvis koden opp i separate instruksjonar som bestemmer kva programmet skal gjere. Når programmet les ei linje og kjem til eit semikolon, oppfattar det semikolonet som slutten på ein instruksjon eller kommando. Mikrokontrolleren utfører då først instruksjonen han akkurat har lese, og begynner så å lese neste linje. Du kan samanlikne semikolon-kommandoen med å trykke linjeskift-tasten ("Enter"-tasten) når du skriv på eit tastatur.
Det er viktig at du bruker semikolon rett i Arduino-koden. Viss du gløymer å skrive semikolon på slutten av ein instruksjon, vil du få melding om syntaksfeil når du prøver å kompilere koden din. Manglande semikolon fører nemleg til at kommandolinjer feilaktig blir slått saman, eller at Arduinoen oppfattar informasjonsteksten som ein del av koden, men utan å kjenne igjen nokon kommandoar, funksjon eller variablar.
Variablar blir brukte til å lagre informasjon. Dei kan beskrivast som boksar som lagrar ein verdi inni seg. Mens eit program køyrer, kan det både legge til og hente ut variabelverdiar.
For at programmet skal skjønne kva som kan puttast i dei ulike variablane, må du fortelje mikrokontrolleren kva type data som skal lagrast i dei.
Talvariablar – heiltal
Ein boks (variabel) som heiter int kan berre ha eit heiltal inni seg (1, 2, 3 osb.). Han kan ikkje innehalde desimaltal.
Talvariablar – desimaltal
Vil du at variabelen skal ha plass til desimaltal, må du velje float eller double. Eit enkelt døme kan vere talet pi. Treng du berre avrundinga 3,14, kan du godt bruke float. Treng du fleire desimalar for å få større nøyaktigheit, bør du nytte double.
Med double kan du i utgangspunktet lagre data med storleik 8 byte, mens float lagrar data med storleik 4 byte. Men små Arduinoar har ikkje stor lagringskapasitet. Dei lagrar derfor talverdiar som 4-bytes-data, same om du har valt float eller double. Skal programmet behandle tal som krev større lagringskapasitet, må du velje ein større Arduino med kraftigare minne.
Char
Variabeltypen char (blir uttalt tsjar) kan berre innehalde eitt enkelt teikn, men dette teiknet kan til gjengjeld vere eit tal, ein bokstav eller kva som helst anna teikn som finst i ASCII-tabellen og dermed på eit vanleg tastatur.
Tekstvariablar (String)
I ein char-variabel kan berre skrive eitt enkelt teikn, ikkje heile ord eller setningar. Viss du vil lagre tekst, kallar du boksen (variabelen) for String.
Fordelen med å bruke String i staden for char er at du enkelt kan lage setningar, søke etter understrenger og utføre andre tekstbehandlingsoppgåver utan at du må handtere enkeltteikn manuelt.
I programmeringsverktøyet er det ikkje lagt inn noka avgrensing for kor lang ein String-variabel kan vere. Den einast avgrensinga er minnekapasiteten til Arduinoen. Ein Arduino Mega kan naturleg nok behandle større String-variablar enn ein Arduino Micro. Eit program som er skrive for ein Arduino Mega, kan derfor gi feilmelding på ein Micro eller Nano fordi dei har for lite minne.
Ver merksam på at bruk av String kan krevje meir minne og prosessorkraft enn bruk av char-variablar, spesielt viss du arbeider med store tekststrenger eller har avgrensa minne på Arduino-eininga di.
Bool
Variablar av typen bool kan berre ha ein av to verdiar, til dømes true eller false, sann eller usann, ja eller nei, 0 eller 1. Slike verdiar blir ofte brukte saman med if-setningar (funksjonar som sjekkar om eit vilkår er oppfylt eller ikkje), til dømes:
Viss lysbrytar er PÅ (TRUE), aktiver utgang for lyspære.
Viss lysbrytar er AV (FALSE), deaktiver utgang for lyspære.
Variabel | Kan innehalde | Døme |
---|---|---|
int | heiltal | int a = 14; int tal = 42; |
double | desimaltal (med mange desimalar) | double b = 3.14159265359; |
float | desimaltal (med få desimalar) | float c = 3.14; |
String | tekst | String namn = ''Ola''; String temperatur = ''Det er varmt.''; |
char | enkeltteikn | char bokstav= 'a'; char kva = '?'; char emneknagg = '#'; |
bool | sant / usant på / av | bool x = true; bool y = false; bool på = 1; bool av = 0; |
Ein array er ei samling variablar av same type, nesten som ei liste. Kvar verdi i ein array har sin eigen indeks, altså si eiga lagringsadresse, og han kan skrivast til eller lesast frå.
Du kan definere kor mange tal ein array skal innehalde (lengde), og kva slags tal det skal vere (type). For å opprette ein array bruker du ein av metodane som er viste under. Det er viktig at du er merksam på den ulike bruken av symbola (), [] og {}. (Dei heiter bogeparentes, hakeparentes og sløyfeparentes).
Med funksjon meiner vi innan programmering ei blokk med kode som utfører ei spesifikk oppgåve, og som kan brukast fleire gonger. Arduino-språket har mange innebygde funksjonar. I tillegg kan vi lage våre eigne funksjonar.
if-setningar
Ei if-setning er ein funksjon som sjekkar om eit vilkår er oppfylt (sant) eller ikkje. Viss vilkåret er oppfylt, vil koden køyrast, viss det ikkje er oppfylt, vil programmet hoppe over koden som er knytt til if-setninga, og gå direkte til neste kodelinje.
Programmering av automatiske gatelys er eit godt døme på bruk av if-setningar: Lysa skal berre skruast på dersom det er mørkare enn grenseverdien sensoren er programmert til. Når det er lysare enn programmert verdi, blir lysa slått av.
else if
Når ein if-funksjon inneheld fleire enn to vilkår, bruker vi else if. Vi kan igjen bruke automatisk gatebelysning som døme. No har vi lagt inn eit ekstra vilkår i koden som gjer det mogleg å slå på belysninga manuelt med ein lysbrytar og overstyre lyssensoren.
For-løkker
For-løkker og løkker generelt bruker vi når vi vil gjenta eit sett med kode fleire gonger. Ei for-løkke startar med "for", etterfølgd av instruksjonar om korleis løkka skal køyre.
While-løkker
Ei while-løkke repeterer koden som er knytt til ho, så lenge eit vilkår er sant. Vilkåret står i bogeparentes ( ).
Oversikt over logiske funksjonar
Logiske funksjonar utfører operasjonar ut frå spørsmål eller påstandar i programkoden din.
Namn | Oppgåve | Døme |
---|---|---|
if | utfører ei blokk med kode viss eit vilkår er oppfylt (sant) | int sensorValue = analogRead(A0); if (sensorValue > 500) { digitalWrite(LED_BUILTIN, HIGH); // Slår på LED viss sensorverdien er over 500 } |
else | utfører ei anna blokk med kode viss vilkåret i ei if-setning ikkje er oppfylt (sant) | int sensorValue = analogRead(A0); if (sensorValue > 500) { digitalWrite(LED_BUILTIN, HIGH); // Slår på LED viss sensorverdien er over 500 } else { digitalWrite(LED_BUILTIN, LOW); // Slår av LED viss sensorverdien er 500 eller mindre } |
else if | tillet at fleire vilkår blir testa etter kvarandre i ei if-else-rekkefølge | |
&& | sann viss begge vilkåra er sanne | int temperature = 25; int humidity = 70; if (temperature > 20 && humidity < 80) { // Utfør handling viss temperaturen er over 20 gradar og luftfukta er under 80 % } |
!! | sann viss minst eitt vilkår er sant | bool isSensorTriggered = !!sensorValue; // Konverterer sensorverdien til ein boolsk verdi |
! | inverterer sanningsverdien til eit vilkår, boolsk verdi | if (!sensorValue) { Serial.println(''Sensoren er aktivert!''); } else { Serial.println(''Sensoren er ikkje aktivert.''); } |
for | gjentek ei blokk med kode eit bestemt antal gonger | for (int i = 0; i < 10; i++) { digitalWrite(LED_BUILTIN, HIGH); // Slår på LED delay(500); digitalWrite(LED_BUILTIN, LOW); // Slår av LED delay(500); } |
while | gjentek ei blokk med kode så lenge eit vilkår er sant (oppfylt) | |
do while | gjentek ei blokk med kode minst éin gong, og deretter så lenge eit vilkår er sant | |
switch | testar ein uttrykksverdi mot fleire tilfelle og utfører kode basert på resultatet | int dayOfWeek = 3; switch (dayOfWeek) { case 1: Serial.println(''Måndag''); break; case 2: Serial.println(''Tysdag''); break; case 3: Serial.println(''Onsdag''); break; // ... osb. } |
Oversikt over matematiske funksjonar
Ein mikrokontroller er òg ein avansert kalkulator. Du kan derfor bruke ei rekke matematiske funksjonar for å gjere kalkulasjonar som er nødvendige for å behandle både variablar og tal.
Namn eller symbol | Oppgåve | Døme |
---|---|---|
+ | legg saman to tal | int sum = x + y; // Addisjon |
– | trekker eit tal frå eit anna | int difference = x – y; // Subtraksjon |
* | multipliserer to tal | int product = x * y; // Multiplikasjon |
/ | deler eit tal med eit anna | int quotient = x / y; // Divisjon |
% | returnerer resten av ein divisjon | int remainder = x % y; // Modulus |
++ | aukar verdien til ein variabel med 1 | count++; // aukar count med 1 |
–– | reduserer verdien til ein variabel med 1 | count––; // reduserer count med 1 |
pow(x, y) | reknar ut x opphøgd i y | |
sqrt(x) | reknar ut kvadratrota av x | float number = 16.0; float squareRoot = sqrt(number); // Kvadratrota av 16 = 4 |
cbrt(x) | reknar ut kubikkrota av x | |
sin(x), cos(x), tan(x) | reknar ut sinus, cosinus og tangens til vinkelen x (i radianar) | float angleradians = 0.7854; // ca. 45 gradar i radianar float sineValue = sin(angleradians); float cosineValue = cos(angleradians); float tangentValue = tan(angleradians); |
asin(x), acos(x), atan(x) | reknar ut invers sinus, invers cosinus og invers tangens | |
round(x) | rundar eit tal opp eller ned til næraste heiltal | float realNumber = 3.7; int rounded = round(realNumber); // Avrundes til 4 |
ceil(x) | rundar opp til næraste heiltal | float realNumber = 3.7; int ceilValue = ceil(realNumber); // Avrundes opp til 4 |
floor(x) | rundar ned til næraste heiltal | float realNumber = 3.7; int floorValue = floor(realNumber); // Avrundes ned til 3 |
abs(x) | returnerer absoluttverdien av x | int number = –5; int absValue = abs(number); // Absoluttverdi av –5 = 5 |
sign(x) | returnerer –1 viss x er negativ, 0 viss x er null og 1 viss x er positiv | int number = –5; |
random(min, max) | genererer eit tilfeldig tal innanfor ei gitt rekkevidde | int randomNumber = random(1, 10); // Tilfeldig tal mellom 1 og 9 |
randomseed(seed) | set ein startverdi for den tilfeldige talgeneratoren | |
float(x) | konverterer ein verdi til variabeltypen float | int intValue = 42; float floatValue = float(intValue); // Konverterer int til float |
int(x) | konverterer ein verdi til eit heiltal (int) | float floatValue = 3.14; int intValue = int(floatValue); // Konverterer float til int |
long(x) | konverterer ein verdi til variabeltypen long | int intValue = 54321 long longValue = long(intValue); // |
PI | representerer π (pi), som er ein matematisk konstant | float circleArea = PI * radius * radius; // Reknar ut arealet til ein sirkel |
Når du har skrive koden, må han kompilerast. Det vil seie at han må konverterast (bli omsett) til eit kodespråk som mikrokontrolleren forstår. Til dette bruker du ein kompilator, eit dataprogram som først analyserer programkoden din og så konverterer han. I denne prosessen gjer kompilatoren følgande:
Parsing: Parsing er sjekk av syntaksen til programkoden. Dette betyr at koden blir sjekka for grammatikk, skrivefeil og feil bruk av spesialteikn som til dømes semikolon og parentesar.
Semantisk analyse: Kompilatoren sjekkar om koden følger strukturen og dei logiske reglane i programmeringsspråket, og om programmet kan utførast slik det er meint. Mellom anna sjekkar han om variablar er riktig deklarerte og brukte, om funksjonar får rette typar argument, og om operasjonar er logisk tillatt.
Optimalisering: Kompilatoren kan utføre ulike typar optimalisering for å forbetre maskinkoden som blir generert. Til dømes kan han sjå etter moglegheiter for å redusere minnebruken eller for å få programmet til å køyre raskare.
Generering av maskinkode: Kompilatoren konverterer den syntaktisk og semantisk rette koden til maskinkode som den aktuelle mikrokontrolleren kan lese og utføre.
Lenking (linking): Viss programmet består av fleire filer, handterer kompilatoren òg lenkinga. Då kombinerer han alle dei genererte maskinkodefilene i ei køyrbar enkeltfil.
Når kompileringsprosessen er fullført, har du ei køyrbar fil (binær fil) som kan lastast opp og køyrast på Arduino-eininga di. I Arduino-miljøet er kompilatoren ein del av programvara som lar deg utvikle og laste opp kode til Arduino-eininga.