Programmering i Arduino
En mikrokontroller har et innebygd kodespråk som forstår et bestemt sett med koder. Programmet ditt må bestå av koder som samsvarer med dette kodesettet, og kodene må stå i riktig rekkefølge. Da forstår mikrokontrolleren hva den skal gjøre.
Når du lager programkoden, kan du bruke et programmeringsverktøy, eller du kan skrive i et tekstbehandlingsprogram.
Fordelen med å bruke programmeringsverktøyet er at du får ulik tekstfarge på ulike deler av programkoden, og det kan gjøre koden mer oversiktlig. Programmeringsverktøyet har også et auto-formatverktøy som kan hjelpe deg å lage tekstinnrykk og formatere koden slik at den visuelt blir enklere å lese.
Når du skriver koden din i et tekstbehandlingsprogram, må du hente den inn i programmeringsverktøyet for kompilering og lagring på Arduinoen. Uansett hvilket verktøy du har brukt, er det først ved kompilering at blir koden feilsjekka mot programmeringsspråket.
Et Arduino-program består alltid av to funksjoner: setup() og loop().
Funksjonen setup() kjøres kun én gang, og det er med en gang bruker kopler til strømmen.
Funksjonen loop() kjører så lenge Arduinoen har strøm. Her skriver du koden for det du vil Arduinoen skal gjøre.
Hvilke kodeblokker som skal plasseres hvor, vil du forstå bedre når du begynner å løse oppgaver.
Når du skriver dobbel skråstrek (// ), vil ikke programmet lese teksten som følger etter skråstrekene, som kode. Dobbel skråstrek bruker du derfor når du vil legge inn kommentarer i koden, enten for å beskrive for andre hva programmet skal gjøre, eller helt enkelt for å ha en huskelapp til deg selv.
I Arduino-programmering og i mange andre programmeringsspråk bruker vi semikolon (;) som et avslutningstegn.
Vi deler vanligvis koden opp i separate instruksjoner som bestemmer hva programmet skal gjøre. Når programmet leser ei linje og kommer til et semikolon, oppfatter det semikolonet som slutten på en instruksjon eller kommando. Mikrokontrolleren utfører da først instruksjonen den akkurat har lest, og begynner så på neste linje. Du kan sammenlikne semikolon-kommandoen med å trykke linjeskift-tasten ("Enter"-tasten) når du skriver på et tastatur.
Det er viktig at du bruker semikolon riktig i Arduino-koden. Hvis du glemmer å skrive semikolon på slutten av en instruksjon, vil du få melding om syntaksfeil når du prøver å kompilere koden din. Manglende semikolon fører nemlig til at kommandolinjer feilaktig blir slått sammen, eller at Arduinoen oppfatter informasjonsteksten som en del av koden, men uten å kjenne igjen noen kommandoer, funksjon eller variabler.
Variabler brukes til å lagre informasjon. De kan beskrives som bokser som lagrer en verdi inni seg. Mens et program kjører, kan det både legge til og hente ut variabelverdier.
For at programmet skal skjønne hva som kan puttes i de ulike variablene, må du fortelle mikrokontrolleren hvilken type data som skal lagres i dem.
Tallvariabler – heltall
En boks (variabel) som heter int kan bare ha et heltall inni seg (1, 2, 3 osv.). Den kan ikke inneholde desimaltall.
Tallvariabler – desimaltall
Vil du at variabelen skal ha plass til desimaltall, må du velge float eller double. Et enkelt eksempel kan være tallet pi. Trenger du bare avrundinga 3,14, kan du godt bruke float. Trenger du flere desimaler for å få større nøyaktighet, bør du benytte double.
Med double kan du i utgangspunktet lagre data med størrelse 8 byte, mens float lagrer data med størrelse 4 byte. Men små Arduinoer har ikke stor lagringskapasitet. De lagrer derfor tallverdier som 4-bytes-data, samme om vi har valgt float eller double. Skal programmet behandle tall som krever større lagringskapasitet, må du velge en større Arduino med kraftigere minne.
Char
Variabeltypen char (uttales tsjar) kan bare inneholde ett enkelt tegn, men dette tegnet kan til gjengjeld være et tall, en bokstav eller et hvilket som helst annet tegn som fins i ASCII-tabellen og dermed på et vanlig tastatur.
Tekstvariabler (String)
I en char-variabel kan bare skrive ett enkelt tegn, ikke hele ord eller setninger. Hvis du vil lagre tekst, kaller du boksen (variabelen) for String.
Fordelen med å bruke String i stedet for char er at du enkelt kan lage setninger, søke etter understrenger og utføre andre tekstbehandlingsoppgaver uten at du må håndtere enkelttegn manuelt.
I programmeringsverktøyet er det ingen begrensninger for hvor lang en String-variabel kan være. Den eneste begrensninga er minnekapasiteten til Arduinoen. En Arduino Mega kan naturlig nok behandle større String-variabler enn en Arduino Micro. Et program som er skrevet for en Arduino Mega, kan derfor gi feilmelding på en Micro eller Nano fordi de har for lite minne.
Vær oppmerksom på at bruk av String kan kreve mer minne og prosessorkraft enn bruk av char-variabler, spesielt hvis du arbeider med store tekststrenger eller har begrensa minne på Arduino-enheten din.
Bool
Variabler av typen bool kan bare ha en av to verdier, for eksempel true eller false, sann eller usann, ja eller nei, 0 eller 1. Slike verdier brukes ofte sammen med if-setninger (funksjoner som sjekker om et vilkår er oppfylt eller ikke), for eksempel:
Hvis lysbryter er PÅ (TRUE), aktiver utgang for lyspære.
Hvis lysbryter er AV (FALSE), deaktiver utgang for lyspære.
Variabel | Kan inneholde | Eksempel |
---|---|---|
int | heltall | int a = 14; int tall = 42; |
double | desimaltall (med mange desimaler) | double b = 3.14159265359; |
float | desimaltall (med få desimaler) | float c = 3.14; |
String | tekst | String navn = ''Ola''; String temperatur = ''Det er varmt.''; |
char | enkelttegn | char bokstav= 'a'; char hva = '?'; char hashtag = '#'; |
bool | sant / usant på / av | bool x = true; bool y = false; bool på = 1; bool av = 0; |
En array er ei samling variabler av samme type, nesten som ei liste. Hver verdi i en array har sin egen indeks, altså sin egen lagringsadresse, og den kan skrives til eller leses fra.
Du kan definere hvor mange tall en array skal inneholde (lengde), og hva slags tall det skal være (type). For å opprette en array bruker du en av metodene vist under. Det er viktig at du er oppmerksom på den ulike bruken av symbolene (), [] og {}. (De kalles bueparentes, hakeparentes og sløyfeparentes).
Med funksjon mener vi innen programmering ei blokk med kode som utfører ei spesifikk oppgave, og som kan brukes flere ganger. Arduino-språket har mange innebygde funksjoner. I tillegg kan vi lage våre egne funksjoner.
if-setning
En if-setning er en funksjon som sjekker om et vilkår (en betingelse) er oppfylt, altså om vilkåret er sant eller ikke. Hvis vilkåret er oppfylt, vil koden kjøres, hvis det ikke er oppfylt, vil programmet hoppe over koden som er knytta til if-setningen, og gå direkte til neste kodelinje.
Programmering av automatiske gatelys er et godt eksempel på bruk av if-setninger: Lysa skal bare skrus på dersom det er mørkere enn hva sensoren er programmert til. Når det er lysere enn programmert verdi, slås lysa av.
else if
Når en if-funksjon inneholder flere enn to vilkår, bruker vi else if. Vi kan igjen bruke automatisk gatebelysning som eksempel. Nå har vi lagt inn et ekstra vilkår i koden som gjør det mulig å slå på belysninga manuelt med en lysbryter og overstyre lyssensoren.
For-løkker
For-løkker og løkker generelt bruker vi når vi vil gjenta et sett med kode flere ganger. Ei for-løkke starter med "for", etterfulgt av instruksjoner om hvordan løkka skal kjøre.
While-løkker
Ei while-løkke repeterer koden som er knytta til den, så lenge et vilkår er oppfylt (sant). Vilkåret står i bueparentes ( ).
Oversikt over logiske funksjoner
Logiske funksjoner utfører operasjoner ut fra spørsmål eller påstander i programkoden din.
Navn | Oppgave | Eksempel |
---|---|---|
if | utfører ei blokk med kode hvis et vilkår er oppfylt (sant) | int sensorValue = analogRead(A0); if (sensorValue > 500) { digitalWrite(LED_BUILTIN, HIGH); // Slår på LED hvis sensorverdien er over 500 } |
else | utfører ei annen blokk med kode hvis vilkåret i en if-setning ikke er oppfylt (usant) | int sensorValue = analogRead(A0); if (sensorValue > 500) { digitalWrite(LED_BUILTIN, HIGH); // Slår på LED hvis sensorverdien er over 500 } else { digitalWrite(LED_BUILTIN, LOW); // Slår av LED hvis sensorverdien er 500 eller mindre } |
else if | tillater at flere vilkår blir testa etter hverandre i en if-else-rekkefølge | |
&& | sann hvis begge vilkåra er sanne | int temperature = 25; int humidity = 70; if (temperature > 20 && humidity < 80) { // Utfør handling hvis temperaturen er over 20 grader og luftfuktigheten er under 80 % } |
!! | sann hvis minst ett vilkår er sant | bool isSensorTriggered = !!sensorValue; // Konverterer sensorverdien til en boolsk verdi |
! | inverterer sannhetsverdien til et vilkår, boolsk verdi | if (!sensorValue) { Serial.println(''Sensoren er aktivert!''); } else { Serial.println(''Sensoren er ikke aktivert.''); } |
for | gjentar ei blokk med kode et bestemt antall ganger | 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 | gjentar ei blokk med kode så lenge et vilkår er sant (oppfylt) | |
do while | gjentar ei blokk med kode minst én gang, og deretter så lenge et vilkår er sant (oppfylt) | |
switch | tester en uttrykksverdi mot flere tilfeller og utfører kode basert på resultatet | int dayOfWeek = 3; switch (dayOfWeek) { case 1: Serial.println(''Mandag''); break; case 2: Serial.println(''Tirsdag''); break; case 3: Serial.println(''Onsdag''); break; // ... osv. } |
Oversikt over matematiske funksjoner
En mikrokontroller er også en avansert kalkulator. Du kan derfor bruke ei rekke matematiske funksjoner for å gjøre kalkulasjoner som er nødvendige for å behandle både variabler og tall.
Navn eller symbol | Oppgave | Eksempel |
---|---|---|
+ | legger sammen to tall | int sum = x + y; // Addisjon |
– | trekker et tall fra et annet | int difference = x – y; // Subtraksjon |
* | multipliserer to tall | int product = x * y; // Multiplikasjon |
/ | deler et tall med et annet | int quotient = x / y; // Divisjon |
% | returnerer resten av en divisjon | int remainder = x % y; // Modulus |
++ | øker verdien til en variabel med 1 | count++; // øker count med 1 |
–– | reduserer verdien til en variabel med 1 | count––; // reduserer count med 1 |
pow(x, y) | beregner x opphøyd i y | |
sqrt(x) | beregner kvadratrota av x | float number = 16.0; float squareRoot = sqrt(number); // Kvadratrota av 16 = 4 |
cbrt(x) | beregner kubikkrota av x | |
sin(x), cos(x), tan(x) | beregner sinus, cosinus og tangens til vinkelen x (i radianer) | float angleRadians = 0.7854; // ca. 45 grader i radianer float sineValue = sin(angleRadians); float cosineValue = cos(angleRadians); float tangentValue = tan(angleRadians); |
asin(x), acos(x), atan(x) | beregner invers sinus, invers cosinus og invers tangens | |
round(x) | runder et tall opp eller ned til nærmeste heltall | float realNumber = 3.7; int rounded = round(realNumber); // Avrundes til 4 |
ceil(x) | runder opp til nærmeste heltall | float realNumber = 3.7; int ceilValue = ceil(realNumber); // Avrundes opp til 4 |
floor(x) | runder ned til nærmeste heltall | 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 hvis x er negativ, 0 hvis x er null og 1 hvis x er positiv | int number = –5; int signValue = sign(number); // –1 (negativt tall) |
random(min, max) | genererer et tilfeldig tall innenfor en gitt rekkevidde | int randomNumber = random(1, 10); // Tilfeldig tall mellom 1 og 9 |
randomSeed(seed) | setter en startverdi for den tilfeldige tallgeneratoren | |
float(x) | konverterer en verdi til typen float | int intValue = 42; float floatValue = float(intValue); // Konverterer et tall av typen int til float |
int(x) | konverterer en verdi til typen int | float floatValue = 3.14; int intValue = int(floatValue); // Konverterer float til int |
long(x) | konverterer en verdi til typen long | int intValue = 54321 long longValue = long(intValue); // |
PI | representerer π (pi), som er en matematisk konstant | float circleArea = PI * radius * radius; // Beregner arealet til en sirkel |
Når du har skrevet koden, må den kompileres. Det vil si at den må konverteres (oversettes) til et kodespråk som mikrokontrolleren forstår. Til dette bruker du en kompilator, et dataprogram som først analyserer programkoden din og så konverterer den. I denne prosessen gjør kompilatoren følgende:
Parsing: Parsing er sjekk av syntaksen til programkoden. Dette betyr at koden sjekkes for grammatikk, skrivefeil og feil bruk av spesialtegn som for eksempel semikolon og parenteser.
Semantisk analyse: Kompilatoren sjekker om koden følger strukturen og de logiske reglene i programmeringsspråket, og om programmet kan utføres slik det er ment. Blant annet sjekker den om variabler er riktig deklarert og brukt, om funksjoner får riktige typer argumenter, og om operasjoner er logisk tillatt.
Optimalisering: Kompilatoren kan utføre ulike typer optimalisering for å forbedre maskinkoden som blir generert. For eksempel kan den se etter muligheter for å redusere minnebruken eller for å få programmet til å kjøre raskere.
Generering av maskinkode: Kompilatoren konverterer den syntaktisk og semantisk riktige koden til maskinkode som den aktuelle mikrokontrolleren kan lese og utføre.
Lenking (linking): Hvis programmet består av flere filer, handterer kompilatoren også lenkinga. Da kombinerer den alle de genererte maskinkodefilene i ei enkelt kjørbar fil.
Når kompileringsprosessen er fullført, har du ei kjørbar fil (binær fil) som kan lastes opp og kjøres på Arduino-enheten din. I Arduino-miljøet er kompilatoren en del av programvaren som lar deg utvikle og laste opp kode til Arduino-enheten.