Läs och förstå arduino kod

Som alla andra språk så behövs övning och det bästa sättet att lära sig är att prata med en urinvånare. Nu är mikrodatorer inte så pratsamma i början men det kommer. Jag kommer att gå igenom ett antal vanliga arduino program som du har stött på eller kommer att stöta på och stega igenom dem så grundligt jag kan. Kontakta mig ifall det är mer jag behöver gå igenom.

Läsa och förstå blink

Det absolut första programmet de flesta stöter på är ett blink  program (arduinos variant).

 

/*
Blink

Turns an LED on for one second, then off for one second, repeatedly.

Most Arduinos have an on-board LED you can control. On the UNO, MEGA and ZERO
 it is attached to digital pin 13, on MKR1000 on pin 6. LED_BUILTIN is set to
 the correct LED pin independent of which board is used.
 If you want to know what pin the on-board LED is connected to on your Arduino
 model, check the Technical Specs of your board at:
 https://www.arduino.cc/en/Main/Products

 modified 8 May 2014 by Scott Fitzgerald
 modified 2 Sep 2016 by Arturo Guadalupi
 modified 8 Sep 2016 by Colby Newman

This example code is in the public domain.
 http://www.arduino.cc/en/Tutorial/Blink
*/

// the setup function runs once when you press reset or power the board
 void setup() {
 // initialize digital pin LED_BUILTIN as an output.
    pinMode(LED_BUILTIN, OUTPUT);
 }

// the loop function runs over and over again forever
 void loop() {
    digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(1000);                       // wait for a second
    digitalWrite(LED_BUILTIN, LOW);    // turn the LED off by making the voltage LOW
    delay(1000);                       // wait for a second
 }

Språket c

Språket som används för att programmera arduino är c och ibland brukar jag skriva en variant av det då språket genomgår förändringar och det är valfritt att välja att gå vidare med exempelvis de förändringar som skett de senaste åren.

Vanligast nuförtiden är att utgå ifrån någon av C89/C90, C99 and C11. Siffrorna efter C står för årtiondet då specifikationen skrevs (C11 = 2011, C99 = 1999) så det är inget språk i förändring precis vars versioner kommer i nya betydande förändringar vart 10 år. Andra yngre språk som javascript förändras någorlunda radikalt varje år. Python är ett språk som börjat att lugna ner sig i stil med C.

Språket c i sig är bara ett språk. Språket saknar de bibliotek som behövs för att kunna skapa arduinospecifik kod. Biblioteken innehåller kod som ger språket vi använder specialord som underlättar att använda arudinon. Det är öppen källkod så om du vill så kan du delta i att förbättra biblioteken. Ofta är det rätt mastig kod men du lär dig massor om du skulle våga dig på att läsa det senare i livet.

Skrivnormer i programmering

Programmering är dialektalt och det finns massor av skrivna och oskrivna regler. Vilket betyder att när du läser kod så kommer det att se olika ut beroende på vem som skriver koden och vad de har för bakgrund eller pratar för andra språk. Oftast försöker man att skriva på engelska även om det absolut inte spelar någon roll för mikrodatorn vad variabler och funktioner kallas för (så länge du minns dem och de som läser förstår vad du mer).

När det gäller namngivning av variabelord, funktioner och så vidare så måste de skrivs utan mellanslag. Sätter du mellanslag i variabeldeklarationen tror kompileraren att du just skrivit två variabler efter varandra. I blinkprogrammet så kan du hitta:

  • digitalWrite()
  • pinMode()
  • LED_BUILTIN

Konventionen de första två kallas för camelCase vilket är vanligast att använda idag. Med camelCase skrivs första ordet med gemen sen därefter efterföljande ord inleds med versaler och då blir det som kamelryggar. Ibland så kommer du att stöta på en äldre konvention som ser ut som LED_BUILTIN men i så fall är det förhistoriska funktioner då de flesta av dem skrevs enligt skrönan att det inte fanns versaler och gemener representerade i datorer på den tiden.

Enkelradskommentarer // och flerradskommentarer /* */

Hela programmet börjar med en introduktionstext som bara är till för dig, dina medförfattare och kommer aldrig att läsas av datorn. Det är nämligen så att kommentarer skalas bort när du kompilerar koden till maskinkod.

Det vanliga förhållningssättet är att introducera programmet med en titel, en ide av vad det ska göra, vem du är och rättigheter. Det är de som du ser i flerraders kommentarerna som börjar med /* och slutar med */.

Det finns också en del enradskommentarer som är den text som följer efter två snedstreck på detta viset: //. Vi kan studera två varianter ifrån programmet.

// the loop function runs over and over again forever
void loop() {
    digitalWrite(LED_BUILTIN, HIGH);   // turn the LED on (HIGH is the voltage level)

När du skriver kod själv så är det alltid rekommenderat att du skriver enkelradskommentarer med vad du förväntar dig att koden nedan ska åstadkomma ungefär som en rubrik. Sen för minnet så kan du efter ditt kommando skriva resultatet av vad raden gör precis som de gjort i kommentaren efter digitalWrite.

Så tar vi bort alla kommentarer så blir programmet bara några rader långt.

void setup() {
     pinMode(LED_BUILTIN, OUTPUT);
 }

void loop() {
     digitalWrite(LED_BUILTIN, HIGH);
     delay(1000);
     digitalWrite(LED_BUILTIN, LOW);
     delay(1000);
 }

Kommentarer är fruktansvärt behändiga i att studera och felsöka din kod. Ibland skriver du något som datorn inte förstår och då får du formulera om dig. Istället för att ta bort det som du just har ägnat en massa tid på att tänka ut så kan du kommentera bort det enkelt med att kommentera ut din kod som inte fungerar.

Funktionerna setup() och loop()

Om du studerar det som är kvar nu så är där två så kallade funktioner kvar med ett antal instruktioner. Du kan se att de är funktioner genom att de börjar med ordet void. Void är ett speciellt ord som ber kompileringen att vänta med att köra innehållet till det att funktionsnamnet kallas. Skriver ett hypotetiskt exempel:

// sätt en led på ett visst portnummer till high
void lampOn(int ledOnIOnumber) {
     digitalWrite(ledOnIOnumber, HIGH);
 }
 lampOn(13);
 lampOn(4);

lampOn() är en liten funktion som slår på en ledlampa på en viss siffra precis som i binkprogramets loop() finns en antal digitalWrite() men tänk om du ska slå på en hel massa ledlampor då kanske du vill skriva mindre och låta koden jobba åt dig. 

Du kommer att stöta på i framtiden att alla funktioner behöver köras precis som funktionen lampOn() här ovan. Med att köra menar jag att du skriver funktionens namn följt av ett kör (;). I koden så slår den på LEDs som är anslutna på 13 och 4. Smidigheten med detta är just det att funktionen kan användas flera gånger.

Nu kommer du kanske att tänka på något verkar udda med setup() och loop() om du tittar på exemplet ovan. Det har du rätt i för att setup() och loop() är specifikt utvalda för att skriva viss typ av arduinokod och körs alltid minst en gång oavsätt om du kör dem eller inte.

Av någon konstig anledning så ser det inte ut som att dessa gör det. Med att de inte verkar köras så är det för att en rad där det står setup(); eller loop(); och så är det för att underlätta att du kommer in i det som är viktigt.

Nu har ju tyvärr inte blink någon annan funktion som vi kan titta närmare på för att blinkprogrammet det är det absolut minsta programmet som du kommer att ha att göra med. Så det blir lite märkligt att just dessa beter sig annorlunda än alla andra funktioner. Men vi kan gräva vidare med vad vi har.

Funktionens objekt

Om du undrar vad måsvingarna {} är för för något så kallas de för objekt. Objekten är grundstenen i programmeringsspråket och skulle kunna ägnas väldigt mycket fokus på men jag ska försöka att hålla det till det väsentligaste.

Så om vi tar det med void setup() { så börjar objektet här och så avslutas det med den andra måsvingen }. Det är ett fantastiskt verktyg för att avgränsa innehållet i koden. Tittar du noga på koden så kommer du att se en viss indentering i början av raden då ett objekt börjar och ju högre i hierarkin som koden går desto större blir indenteringen. Vanligen är indenteringen fyra mellanslag eller en tab. Då tab används till en hel del annat i navigering av gränssnitt så har det sakta börjat att tyna bort samt en latare variant med bara två mellanslag börjat att komma fram.

void lampTwoOff() {
    // dubbelkolla att inte lampan redan är avstängd först
    if ( digitalRead(2) == HIGH ){
        // stäng av lampan
        digitalWrite(2, LOW);
    }
}

Ungefär som ovan kan det då se ut. Normalt så kan det bli ett antal fler nivåer. När det börjar bli ohållbart många (mer är sju tycker jag själv är helt oläsligt) så är det dags att bryta loss koden och skriva nya funktioner för att underlätta läsbarheten av koden.

Variabler, int och andra datatyper

Nu använder detta blinkprogramet en specialare i koden som ni kan läsa er till nämligen LED_BUILTIN. Det som döljer sig är en specialfunktion som själv listar ut beroende på vad kompileringen vet om din specifika hårdvara. I arduino uno och liknande så sitter det en inbyggd led på pin 13. C är ett så kallat typat språk vilket innebär att du behöver specificera vad det är för typ av innehåll som du kommer att fylla dina variabler med. Den allra vanligaste variabeltypen du kommer att hantera är int. int är kort för integer och står för ett 16-bitars heltal. Vilket betyder att det största talet är (2^15) -1 = 32767 och lika långt negativt. Ska du ha större tal så får du använda 32 bitars typen long.

Det är en strikt datamodell vilket innebär att du ska lagra rätt innehåll till rätt typ. Du måste alltså lagra siffror i en int och bokstav i en char annars så blir kompileringen ledsen. Med tiden kommer du att bli bekant och arbeta med flertalet andra datatyper. Om jag minns rätt så finns det ett trettiotal olika typer. Jag raddar upp några här som en lista och så får du lära känna dem på egen hand allt eftersom du behöver dem:

float, double, short, unsigned short, long, unsigned long, int[3]
char, unsigned char

Nu kommer den spännande biten för att precis som med funktioner så får du lov att kalla dina dataobjekt precis vad du vill så länge som de bara innehåller bokstäver och _ understreck. Om du minns exempelkoden jag skrev för att beskriva en funktion vars första rad såg ut såhär

void lampOn(int ledOnIOnumber) {

Så skapade jag en variabel där när jag skrev: int ledOnIOnumber. Nu vet du att en int normalt sett är ett tal mellan -32767 och 32767. Vilket är mer än väl lagom rymd för att skriva de få heltalen på en arduinos digitala ingångar som sträcker sig från 0 till 13. Nu kanske inte ledOnIOnumber var det tillsynes lättaste namnet att namnge sin variabel till.

Vänster och höger om likhetstecken

Återigen så avsaknad detta programmet likhetstecken men i alla extra exempel så har jag använt dem. Det fungerar som i matematiken.

int a = 1;

Grön till vänster och röd till höger. Det är nämligen så att det på den vänstra sidan skickas in till vänster. Du läser alltså från höger till vänster. Studera detta nedan.

int a = 2;
int b = 3;
a = b; // a blir då 3

Lägg märke till att du behöver bara skapa variabeln en gång. Gör du det två gånger så kommer kompileringen att bli ledsen.

Arduinos egna ordlista

Nu är du rustad så gott det går här och får läsa mer på egen hand. När du använder arduino IDE för att kompilera koden till maskinkod så använder du c i kombination med arduinos egna bibliotek. Det döljer sig en hel del kod bakom varje ord som är specifikt för arduino. Ett bibliotek är en samling av funktioner som är skrivna för de allra vanligaste metoderna att jobba mot exempelvis arduinot. Det första som man bör göra för att förstå ett bibliotek är att att studera vad det finns för ord. Genom att förstå dessa orden kan du göra precis allt utan att skriva avancerad kod. Det finns utförliga beskrivningar av allt som biblioteket innehåller på arduinos hemsida. Besök Language Reference documentation redan nu.