Arduino-projekt 008 - Styr motorer med motor shield

En robot behöver kunna röra sig och för det behöver man motorer. Men motorer kräver mycket ström och det går inte att koppla in dem direkt till ett Arduino-kort. Därför behöver man en motor-drivare av något slag. Den enklaste att använda är Arduino Motor Shield R3. Efterom detta är en s.k. sköld (shield) så passar den att monteras direkt på (eller under) ett standard Arduino-kort. Man behöver därför inte koppla massa sladdar på en kopplingsplatta. Kretsen på det här kortet heter L298P och kan driva två likströmsmotorer på 2A stycket vilket är gott och väl tillräckligt för mindre robotar. För att styra varje motor med det här kortet använder man sig av tre signaler kallad PWM, DIR och BREAK. PWM använder man för att sätta hastigheten på motorn, DIR för att välja riktining och BREAK kortsluter motorn (på säkert sätt) vilket fungerar som en broms. Som namnet antyder så styr man hastigheten på PWM-anslutningen med pulsbreddsmodulering (PWM), dvs du använder funktionen analogWrite().

Kopplingen

Montera motor-skölden ovanpå Arduino-kortet och koppla in motorer och batterier (eller annan strömförsörjning) för motorerna enligt bild nedan. Batterierna behöver ha samma totala spänning som motorerna är gjorda för (men max 12V). Arduinon kan ta ström direkt från batterierna, men för säkrare drift är det bäst att ge ström till Arduinon separat. Annars finns det risk att när motorerna drar mycket ström så sjunker spänningen så pass mycket att Arduino-kortet startar om. Den här uppkopplingen och koden nedan förväntar sig att roboten använder s.k. tank-styrning där hjulen på var sida drivs av var sin motor och roboten svänger genom att ändra hastighet/riktning på motorn (motorerna) på ena sidan. Detta är det absolut vanligaste sättet att driva och styra mindre robotar och det här projektet lämpar sig mycket väl för de flesta av våra robotchassin. För robotar som har fyra motorer (två på var sida) kopplar man in båda motorerna på var sidan till samma utgångar på motor-skölden.

Arduino-projekt 008 – Styr motorer

Beskrivning av koden

Koden är enkel och gör inte något direkt användbart, men fungerar bra för att testa kopplingen och bildar en bra grund för dina egna robotar. Först definierar vi vilka anslutningar på Arduino-kortet som används till vad. Dessa bestäms så klart av själva motor-skölden och du ska inte ändra dem, men vi lägger in dem som konstanter så att vi i resten av koden inte behöver ange siffrorna utan istället kan skriva namnen. Det är mycket lättare att komma ihåg dir_a än att den funktionen ligger på pinne 12. Motor-skölden har ytterligare några anslutningar, men dessa är inte nödvändiga att använda. Därefter skapar vi ett antal funktioner för att sätta hastighet på motorerna och för att köra fram, bak, svänga och att stanna. Stanna-funktionen är egentligen inte nödvändig eftersom det enda den gör är att sätta hastigheten till 0, men den gör resten av koden lite mer lättläst.

I setup() ställer vi bara in vilka anslutningar på Arduino som ska vara utgångar, dvs de fyra kontrollsignaler som motor-skölden använder. I loop() ligger själva huvud-algoritmen. Denna är mycket enkel och använder de funktioner vi skapade för att köra framåt och bakåt och svänga runt i en enkel sekvens. Detta är bara till som exempel och för att se att allt fungerar som det sak. Denna kod ersätter du sedan lämpligen med din egna mer spännande kod. T.ex. kan du låta roboten ta emot kommandon från en bluetooth-modul, eller så kan du koppla in en ultraljudssensor så att roboten kan köra runt själv och undvika hinder.

Om roboten går fel

Om roboten skulle visa sig inte röra sig som du tänkt dig så finns det många möjliga orsaker. Vilket håll som en likströmsmotor snurrar på beror på vilket håll som strömmen genom den går. Så om en motor snurrar åt fel håll räcker det med att byta plats på de två sladdarna till den. Så om roboten svänger åt vänster när den ska gå framåt så byt pats på sladdarna till den vänstra motorn; och omvänt om den svänger åt höger. Om roboten går bakåt när den ska gå framåt byter du plats på sladdarna på båda motorerna, men håll reda på så att du inte växlar runt sladdarna mellan motorerna. Den ena motorn ska fortfarande använda anslutningarna A+ och A- och den andra ska använda B+ och B-. Om roboten går framåt och bakåt korrekt, men svänger höger när den egentligen ska svänga vänster och vice versa så betyder det att motorena sitter på fel sida. Man skulle kunna tro att det då räcker att byta plats på motorerna, men om du gör det kommer roboten att gå bakåt när den ska gå framåt. Istället behöver du byta plats på sladdarna så att motorn som är kopplad till utgångarna A+ och A- kopplas till utgångarna B+ och B- (utan att byta plats på + och -) och att motorn kopplad till B+ och B- kopplas in på A+ och A- (återigen så att + och - bibehålls). Om din robot har fyra motorer, dvs två på var sida, och ena motorn på en sida snurrar åt fel håll byter du runt sladdarna på bara den motorn. Se också till att båda motorerna på en sida är kopplade till samma utgångar, t.ex. motorerna på vänster sida till A+ och A- och motorerna på höger till B+ och B-.

Koden

// Inställningar för motorsignaler. Förutbestämda av motor-skölden så ändra ej!
const int dir_a = 12;
const int dir_b = 13;
const int pwm_a = 3;
const int pwm_b = 11;

// Funktion för att sätta hastigheten på båda motorerna
void hastighet(int v) {
  analogWrite(pwm_a, v);
  analogWrite(pwm_b, v);
}

// Funktion för att köra framåt
void kor() {
  digitalWrite(dir_a, HIGH);
  digitalWrite(dir_b, HIGH);
}

// Funktion för att backa
void backa() {
  digitalWrite(dir_a, LOW);
  digitalWrite(dir_b, LOW);
}

// Funktion för att svänga åt vänster
void svang_vanster() {
  digitalWrite(dir_a, LOW);
  digitalWrite(dir_b, HIGH);
}

// Funktion för att svänga åt höger 
void svang_hoger() {
  digitalWrite(dir_a, HIGH);
  digitalWrite(dir_b, LOW);
}

// Funktion för att stanna
void stanna() {
  hastighet(0);
}

// I setup ställer vi in vilka anslutningar som är utgångar
void setup() {
  // Sätt alla kontrollpinnar till utgångar
  pinMode(dir_a, OUTPUT);
  pinMode(dir_b, OUTPUT);
  pinMode(pwm_a, OUTPUT);
  pinMode(pwm_b, OUTPUT);
}

// Loop kör en enkel sekvens där roboten kör fram, bak och svänger efter ett enkelt mönster.
// Här lägger du in din egen kod för att få roboten att göra mer intressanta saker.
void loop(){
  // Kör framåt i full fart i 5 sekunder
  hastighet(255);
  kor();
  delay(5000);
  // Backa i lägre hastighet i 2 sekunder
  hastighet(180);
  backa();
  delay(2000);
  // Snurra runt åt vänster i 5 sekunder
  hastighet(255);
  svang_vanster();
  delay(5000);
  // Stanna och stå still i 4 sekunder
  stanna();
  delay(4000);
}

Arduino-projekt 007 - Multitasking

För enklare projekt är det inga problem att göra en sak åt gången och vänta på att den är färdig innan man gör nästa, men för lite mer avancerade projekt behöver man ibland göra flera saker samtidigt, s.k. multitasking. T.ex. vill man kanske läsa in data från en radiomottagare, driva runt en stegmotor, blinka med en lysdiod och visa statustext på en skärm samtidigt. Det finns flera sätt att åstadkomma detta, men den metod vi ska visa här är väldigt populär eftersom den är förhållandevis enkel att förstå och programmera och ger bra resultat. Metoden är faktiskt inspirerad av hur professionella industriella kontrollsystem arbetar och därmed välbeprövad. Grundidén bygger på att man ser till att alla uppgifter som Arduino-kortet ska utföra hålls väldigt små så att de går väldigt snabbt att utföra och att man aldrig någonsin aktivt väntar på något. Det innebär att man absolut inte får använda funktionen delay() i sin kod eftersom detta kommer helt att blockera alla andra uppgifter som annars skulle köras samtidigt. Istället så låter man processorn hela tiden jämföra timer-värden och utföra de olika uppgifterna när lagom mycket tid har förflutit.

Hur går det till?

Enkelt förklarat så har man en serie med if-satser som jämför timer-värden som man låter processorn kör igenom om och om igen så snabbt som möjligt. När ett timern överstiger ett inställt tröskelvärde så anropar man den funktion som är kopplad till det tröskelvärdet. Funktionen körs och hoppar sedan tillbaka till huvudprogrammet med if-satser. Tröskelvärdet ställs om till nästa tidpunkt man vill köra funktionen och sedan fortsätter huvudprogrammet att köra runt.

Kopplingen

I vårt exempel ska vi få tre lysdioder att blinka i olika takt. Detta kan kanske verka enkelt först, men vill vi att de ska blinka helt oberoende av varandra behöver vi använda multitasking av någon form. Börja med att bygg upp kopplingen i bilden nedan. Skriv sedan in koden längst ner på sidan i Arduinos utvecklingsmiljö och ladda upp den till Arduino-kortet.

Arduino-projekt 007 – Multitasking

Beskrivning av koden

I koden sätter vi upp tre separata funktioner, blinka_1()blinka_2() och blinka_3() som vi kommer att köra parallellt. Du kan självklart använda den här koden som grund till andra projekt och byta ut funktionerna mot dina egna eller lägga till fler. Vi sätter även upp ett antal variabler för att hålla reda på hur ofta varje funktion ska köra och för att hålla reda på i vilket tillstånd varje lysdiod befinner sig i (tänd eller släckt). Huvudprogrammet ligger i vanlig ordning i loop() och denna kod kommer att köras många tusen gånger i sekunden, så snabbt som det bara är möjligt. Loopen börjar med att vi läser ut aktuell tid med funktionen millis(). Denna funktion lämnar ifrån sig antalet millisekunder sedan Arduino-kortet fick ström, eller sedan senaste resetten. Sedan följer ett tre separata if-satser där vi jämför aktuell tid med de olika timer-värdena för blink-funktionerna. Allra första gången som koden körs kommer dessa att vara 0 och därmed kommer alla tre if-satserna att vara sanna och alla tre blink-funktionerna kommer att köras. När varje blink-funktion körts ställer vi om timer-värdena till att vara aktuell tid plus så många millisekunder som vi vill vänta innan vi kör funktionen igen. Nästa gång loopen körs igenom så kommer aktuell_tid att ha ökat lite, exakt hur mycket beror på hur lång tid det tog att köra igenom de tre funktionerna, men inte så mycket att aktuell_tid är större än någon av timer-variablerna. Därmed kommer alla if-satser att vara falska och inte köras. Detta upprepas nu om och om igen tills aktuell_tid har ökat så mycket att den är större än något av timer-värdena och den associerade funktionen körs och dess timer-värde ställs om på nytt. Detta kommer sedan upprepas i oändligheten. Större delen av tiden så kommer processorn inte att göra något annat än snurra runt i huvudloopen och jämföra värden, men lite då och då kommer den att köra en av blink-funktionerna och sedan hoppa tillbaka och fortsätta snurra runt. Det är viktigt att komma ihåg att även om den här metoden får det att verka som att processorn gör flera gånger samtidigt så kan den aldrig göra mer en en enda sak i taget. Det är bara att den är så väldigt snabb att det verkar som att saker sker parallellt. Det är detta som gör att det är så viktigt att alla funktionerna är snabba och att man aldrig använder delay() för då kommer processorn att bli uppbunden och kommer inte att kunna köra övriga funktioner.

Koden

// Inställning för vilka pinnar som lysdioderna är inkopplade till
const int ledpin_1 = 2;
const int ledpin_2 = 3;
const int ledpin_3 = 4;

// Blinkhastigheter för lysdioderna i millisekunder. 
const int blinktid_1 = 500;
const int blinktid_2 = 400;
const int blinktid_3 = 150;

// Tillståndsvariabler för lysdioderna.
// Dessa håller reda på om de individuella lysdioderna är tända eller släckta.
int led_1 = LOW;
int led_2 = LOW;
int led_3 = LOW;

// Timer-variabler för lysdioderna.
// Dessa håller reda på när det är dags att ändra status på var lysdiod.
double ledtimer_1;
double ledtimer_2;
double ledtimer_3;

// Funktion för att tända eller släcka lysdiod 1
void blinka_1() {
  if (led_1 == LOW) {
    led_1 = HIGH;
  }
  else {
    led_1 = LOW;
  }
  digitalWrite(ledpin_1, led_1);
}

// Funktion för att tända eller släcka lysdiod 2
void blinka_2() {
  if (led_2 == LOW) {
    led_2 = HIGH;
  }
  else {
    led_2 = LOW;
  }
  digitalWrite(ledpin_2, led_2);
}

// Funktion för att tända eller släcka lysdiod 3
void blinka_3() {
  if (led_3 == LOW) {
    led_3 = HIGH;
  }
  else {
    led_3 = LOW;
  }
  digitalWrite(ledpin_3, led_3);
}

// Setup ser till att pinnarna som lysdioderna är kopplade sätts till utgångar.
void setup() {
  pinMode(ledpin_1, OUTPUT);
  pinMode(ledpin_2, OUTPUT);
  pinMode(ledpin_3, OUTPUT);
}

// Loop kommer att köras igenom om och om igen så snabbt det bara är möjligt.
// Om en funktion tar lång tid på sig kommer det att hindra övriga funktioner
// från att köras när de ska.
void loop() {
  // Hämta in aktuell tid. Detta värde är antalet millisekunder sedan
  // strömmen kopplades in till Arduino-kortet, eller sedan senaste resetten.
  double aktuell_tid = millis();
  // Kolla om det är dags att ändra status på lysdiod 1
  if (aktuell_tid > ledtimer_1) {
    blinka_1();
    // Ställ om timer för LED 1 till nästa händelse
    ledtimer_1 = aktuell_tid + blinktid_1;
  }
  // Kolla om det är dags att ändra status på lysdiod 2 
  if (aktuell_tid > ledtimer_2) {
    blinka_2();
    // Ställ om timer för LED 2 till nästa händelse 
    ledtimer_2 = aktuell_tid + blinktid_2;
  }
  // Kolla om det är dags att ändra status på lysdiod 3
  if (aktuell_tid > ledtimer_3) {
    blinka_3();
    // Ställ om timer för LED 3 till nästa händelse 
    ledtimer_3 = aktuell_tid + blinktid_3;
  }
}

Arduino-projekt 006 - Visa text på LCD-skärm

Ibland vill man kunna ge mer information till användaren av sina prylar än vad man kan göra med några lysdioder. Då är en text-LCD-skärm en mycket smidig lösning. Dessa skärmar kan visa text och enkel grafik. De finns i olika storlekar och färger men de vanligaste har 2 rader med 16 tecken, eller 4 rader med 20 tecken och är blå/vita eller grön/svarta. Vissa har även inbyggd belysning så att de går att läsa även i mörker. Dessa display har normalt väldigt många anslutningar för att styra dem, men det finns mycket billiga moduler med PCF8574-chip som löds fast baktill på displayen som ger ett I²C-gränssnitt istället så att hela displayen kan styras med enbart 2 ledningar.

Kodbibliotek

I det här exemplet använder vi oss av en 16x2-teckens display med bakgrundsbelysning och med en I²C-modul, men koden går lätt att anpassa för andra skärmar. Det enda kravet är att de använder styrkretsar som är kompatibla med den mycket populära Hitachi HD44780-kretsen. Det kod-bibliotek vi använder följer inte med Arduino-miljön, men går att installera med ett par klick. Starta Arduino-programmet, gå till menyn Skiss > Inkludera bibioktek > Hantera bibliotek... Detta öppnar upp bibliotekshanteraren och här kan du söka efter biblioteket "LiquidCrystal_PCF8574". När du hittat det klickar du på det och väljer installera. När biblioteket laddats ner och installeras kan du stänga bibliotekshanteraren. Detta behöver du bara göra en gång på datorn, därefter finns det tillgängligt.

Kopplingen

Koppla in displayen till Arduino-kortet enligt bilden nedan.

Arduino-projekt 006 - Visa text på LCD-skärm

Beskrivning av koden

Koden innehåller inga direkt konstigheter. Först hämtar vi in kod-bibiokteket för att styra skärmen och skapar ett objekt för skärmen som håller ordning på inställningar och tillhandahåller alla funktioner som behövs, såsom skriva text, radera och styra bakgrundsbelysning. I setup() startar vi igång skärmen och gör alla nödvändiga förberedelser, såsom att rensa skärmen, slå på bakgrundsbelysning och skriva ut en första text-rad. Huvudprogrammet består av en for-loop som skapar en variabel i som sätts till 10. Sedan kör den sin kod och efteråt minskar den i med 1 (i--) och om i fortfarande är större än noll så körs koden en gång till. Detta innebär att koden i for-loopen kommer att köras 11 gånger och räkna från 10 till 0. Koden inne i for-loopen rensar varje gång andra raden på skärmen och skriver ut räknarens värde dvs variabeln i och väntar sedan en sekund. När for-loopen är färdig så rensar vi skärmen helt och skriver ut ett nytt meddelande. Det sista som sker är att vi startar en while-loop med ett konstant sant värde och utan kod. Detta är lite av en specialkonstruktion som gör att mikrokontrollern i princip hänger sig och kommer inte att göra något mer förrän man nollställer den med reset-knappen eller bryter strömmen till den.

Koden

// Hämta in kod-biblioteket för styrning av ASCII-displayer via I2C
#include <LiquidCrystal_PCF8574.h>

// Skapa objekt för att styra displayen
// Använd I2C-adress 0x27 (standard)
LiquidCrystal_PCF8574 skarm(0x27);

void setup() {
  // Starta skärmen och ange storlek - 16 tecken i 2 rader.
  skarm.begin(16, 2);
  // Rensa skärmen från eventuell gammal text
  skarm.clear();
  // Sätt markören längst upp till vänster
  skarm.home();
  // Aktivera skärmens bakgrundsbelysning med max ljusstyrka
  skarm.setBacklight(255);
  // Skriv text på första raden
  skarm.print("Countdown...");
}

void loop() {
  for (int i = 10; i > 0; i--) {
    // Flytta markören till början av andra raden (tecken 0, rad 1)
    skarm.setCursor(0, 1);
    // Rensa raden genom att skriva 16 mellanslag
    skarm.print("                ");
    // Flytta tillbaka markören till början av raden
    skarm.setCursor(0, 1);
    // Skriv räknarvärde
    skarm.print(i);
    // Vänta en sekund
    delay(1000);
  }
  // Rensa skärmen
  skarm.clear();
  // Sätt markören en bit in på första raden (tecken 4, rad 0)
  skarm.setCursor(4, 0);
  // Skriv ut slut-text
  skarm.print("KA-BOOM!");
  // Stoppa programmet
  while (1);
}

Arduino-projekt 005 - Styr en servomotor

Att styra positionen på ett servo är i grunden väldigt likt att styra ljusstyrkan på en lysdiod. Kopplingen är därför mycket lik den som vi använde i projekt 004. Precis som då är den totala resistansen på vridmotståndet inte så viktigt så länge det är på mellan 1KΩ och 100 KΩ.

Koppling

För vårt projekt behöver du göra följande koppling.

Arduino-projekt 005 - Servo

Beskrivning

Ett servo förväntar sig en puls-bredds-modulerad (PWM) signal precis som vi använde för att styra lysdioden i projekt 002 och 004, men servon har lite större krav på signalen och för att lösa detta finns det färdig kod tillgänlig så att vi inte själva behöver lösa det varje gång vi ska styra ett servo. Det första vi gör i vår kod är därför att inkludera denna färdiga kod och skapar sedan ett objekt som representerar servot. Ett objekt är i princip en variabel men som även har olika funktioner kopplad till sig som gör att man kan utföra olika operationer på datan som variabeln innehåller. I det här exemplet använder vi funktionen attach() för att berätta vilken utgång som servot är inkopplat till och write() för att sätta position på servot. Positionen anges som en vinkel mellan 0 och 180 grader. Detta innebär att vi måste skala om värdet vi läser av från vridmotståndet precis som i projekt 004.

Koden

// Hämta in kod-biblioteket för styrning av servomotorer
#include <Servo.h>

// Skapa ett servo-objekt för att kontrollera servomotorn.
Servo mitt_servo;

// Reservera minne för variabler att lagra data i
int varde_in;
int vinkel;

void setup() {
  // Berätta vilken utgång som ditt servo är inkopplat på.
  mitt_servo.attach(2);
  // Ställ om pinne A0 till att vara en ingång.
  pinMode(A0, INPUT);
}

void loop() {
  // Läs av vridmotståndets position. Detta ger ett värde mellan 0 och 1023.
  varde_in = analogRead(A0);
  // Skala om värdet så att det är mellan 0 och 180.
  vinkel = map(varde_in, 0, 1023, 0, 180);
  // Sätt servomotorns position
  mitt_servo.write(vinkel);
  // Vänta lite så att servot hinner flytta sig till rätt position
  delay(15);
}

Arduino-projekt 004 - Dimmer

I projekt 003 såg vi hur man läser in digitala händelser utifrån, dvs något som är aktivt eller inte. I det här projektet ska vi lära oss hur man läser in analoga värden, dvs ett värde som kan vara vad som helst, inte bara ett eller noll, t.ex. vattennivå, ljusstyrka eller avstånd. För att börja enkelt kommer vi att göra en dimmer som läser av positionen av ett vridmotstånd och sätter sedan ljusstyrkan på en lysdiod till motsvarande värde.

Koppling

Kopplingen vi behöver är lik den från projekt 001, men har även ett vridmotstånd inkopplat till en av de analoga ingångarna. Vridmotståndets värde är inte särskilt viktigt i det här fallet, men bör vara någonstans mellan 1KΩ och 100 KΩ.

Arduino-projekt 004 - Dimmer

Beskrivning

Koden är ganska enkel - vi läser in spänningen på den analog ingången A0 med analogRead() som ett heltal mellan 0 och 1023 och detta använder vi sedan för att sätta ljusstyrkan på lysdioden. Eftersom Arduinos utgångar arbetar med värden mellan 0 och 255 så använder vi oss av map()-funktionen för att skala om värdet. Till skillnad från projekt 003 så aktiverar vi inte pull-up-motståndet på ingången. Vridmotståndet är kopplad till både plus och minus vilket innebär att dess utgång och därmed Arduinons ingång kommer alltid att ha en bestämd spänning och kommer inte att fånga upp störningar. Notera också att det är enbart de dedikerade analoga ingångarna A0-A5 som kan mäta spänning. Alla andra ingångar är digitala och kan därför bara se om spänningen är hög eller låg, dvs logisk etta eller nolla.

Koden

// Reservera minne för variabler att lagra data i
int varde_in;
int varde_ut;

void setup() {
  // Ställ om pinne 2 till till att vara en utgång.
  pinMode(2, OUTPUT);
  // Ställ om pinne A0 till att vara en ingång.
  pinMode(A0, INPUT);
}


void loop() {
  // Läs av vridmotståndets position. Detta ger ett värde mellan 0 och 1023.
  varde_in = analogRead(A0);
  // Skala om värdet så att det är mellan 0 och 255.
  varde_ut = map(varde_in, 0, 1023, 0, 255);
  // Sätt lysdiodens ljusstyrka.
  analogWrite(2, varde_ut);
}

Arduino-projekt 003 - Växlande lysdiod

Detta projekt visar grunderna i hur man får sin Arduino att reagera på yttre händelser. I detta fall använder vi oss av en tryckknapp för att växla en lysdiod mellan tänd och släckt. Kopplingen liknar den för projekt 001, men har även en tryckknapp.

Kopplingen

Precis som i projekt 001 så behöver lysdioden kopplas på rätt håll, dvs det långa benet kopplas till motståndet och det korta till jord (minus).

Arduino projekt 003 - växlande lysdiod

Vid uppstart

Vi börjar programmet med att ställa in in- och utgången. Ingången sätts till "INPUT_PULLUP" vilket innebär att man aktiverar det så kallade pull-up-motståndet för just den ingången. Detta är helt enkelt ett motstånd på några kiloohm kopplat mellan ingången och plus (Vcc) vilket garanterat att ingången alltid är hög (logisk etta). Arduinons ingångar är oerhört känsliga och om inget är inkopplat på dem kommer de att fånga upp störningar från lampor, radiosändare och annat och ingången kommer att växla mellan hög och låg av sig självt. Pull-up-motståndet hindrar detta genom att tvinga ingången till att alltid vara etta om inget annat är inkopplat. I vår koppling så är ingången kopplad till en tryckknapp, men när knappen inte är intryckt så finns det ingen spänning på ingången och utan pull-up-motståndet skulle alltså ingången fånga upp  störningar. När knappen väl trycks in kommer ingången dras ner till minus och bli låg. Detta skulle man kunna tycka skulle innebära en kortslutning eftersom ingången ju är tvingad upp till plus, men eftersom det görs med ett ganska stort motstånd så kommer väldigt lite ström att gå igenom motståndet och kretsen kommer inte att bli skadad.

Huvudprogrammet

Resten av programmet består av Arduinos vanliga loop()-funktion som kör sin kod om och om igen så snabbt det går. Loopen läser in tryckknappens tillstånd och om knappen trycks ner kommer ingången att bli låg. Att ingången blir låg kan kanske verka lite omvänt men det är en konsekvens av pull-up-motståndet som alltid tvingar upp ingången till plus (hög) och att tryckknappen därför behöver kopplas till minus. Om ingången är låg så kontrollerar koden om variabeln lysdiodstatus är 1 eller 0. lysdiodstatus ändras därefter till motsatt värde och utgången för lysdioden sätts antingen till hög eller låg. Det sista som händer är att koden väntar 250ms innan den fortsätter. Denna korta fördröjning krävs eftersom mikrokontrollern är otroligt snabb och kommer hinna köra koden i loop() tusentals gånger i sekunden och om knappen trycks ner så kommer lysdioden växla läga mellan tänd och släckt så snabbt att det är omöjligt att se. Fördröjningen skyddar oss även mot ett annat fenomen som är en vanlig fälla för nybörjare: kontaktstuds. När en tryckknapp trycks ner, eller en strömbrytare slås till så sker detta inte snyggt och prydligt utan kontaktelementen kommer att studsa mot varandra lite. För mänskliga ögat sker detta så snabbt att vi inte märker det, men för en mikrokontroller som kan läsa av knappen många tusen gånger i sekunden så kommer det se ut som att man trycker ner och släpper knappen många gånger väldigt snabbt. Detta går att lösa med elektronik, men om man använder en mikrokontroller brukar man istället göra det med kod eftersom det är billigare. Det finns många sätt att lösa det på och en fördröjning som i vårt exempel är ett av de enklaste.

Koden

// Reservera minne för variabler att lagra data i
int lysdiodstatus = LOW;
int knappstatus;

void setup() {
  // Ställ om pinne 2 till till att vara en utgång.
  pinMode(2, OUTPUT);
  // Ställ om pinne 3 till att vara en ingång med pull-up motstånd
  pinMode(3, INPUT_PULLUP);
}


void loop() {
  // Läs in tryckknappens tillstånd.
  knappstatus = digitalRead(3);
  // Om knappen trycks ner blir ingången låg (LOW)
  if (knappstatus == LOW) {
    if (lysdiodstatus == LOW) {
      lysdiodstatus = HIGH;
    }
    else {
      lysdiodstatus = HIGH;
    }
    digitalWrite(2, LOW); 
    delay(250);
  }
}

Arduino-projekt 002 - Pulserande lysdiod

En blinkande lysdiod är kanske inte det mest spännande, men om man kan få den att pulsera istället vore det lite mer intressant. Gör kopplingen som i projekt 001, men använd nu följande kod istället. Här använder vi oss av en for-loop för att gradvis öka lysdiodens ljusstyrka och när vi nått max så använder vi en ny loop som sänker ljusstyrkan gradvis igen.

Analoga värde i en digital värld

Tidigare använde vi digitalWrite() för att aktivera lysdioden, men med den funktionen kan vi bara ställa utgången till antingen 0 volt eller full matningsspänning dvs 5 volt. För att kunna ställa ljusstyrkan kan vi istället använda oss av analogWrite() som gör det möjligt att sätta en utgång till att till synes ge ut valfri spänning mellan 0 och 5 volt i 256 steg. Det är viktigt att notera att Arduino egentligen inte kan ge ut något annat än digtala signaler, dvs 0 eller 5 volt, men genom att snabbt pulsera utgången så kommer det att se ut som att man får ut en annan spänning. Detta kallas PWM - Pulse Width Modulation, eller pulsbreddsmodulering på svenska. För det här projektet med vår lysdiod spelar det ingen roll eftersom pulserna är för snabba för våra ögon att se och detsamma gäller om man vill styra en motor - pulserna är för snabba för att motorn ska vibrera utan kommer istället att rotera med en hastighet som motsvarar pulserna. Däremot finns det tillfällen när detta inte kommer att fungera så det är viktigt att undersöka om det man kopplar in till Arduino-kortet verkligen klarar av PWM-signaler eller kräver en riktig analog signal. För att skapa en riktig analog signal krävs en speciell krets eller modul som logiskt nog kallas "digital-till-analog omvandlare" eller DAC (Digital to Analog Converter).

Koden

void setup() {
  // Ställ om pinne 2 till till att vara en utgång.
  pinMode(2, OUTPUT);
}

void loop() {
  // Öka ljusstyrkan på lysdioden från helt släckt till helt tänd
  for (int i = 0; i < 255; i++) {
    // Ställ in lysdiodens ljusstyrka
    analogWrite(2, i);
    // Vänta 10ms
    delay(10);
  }
  // Minska ljusstyrkan på lysdioden från helt tänd till helt släckt
  for (int i = 255; i > 0; i--) {
    // Ställ in lysdiodens ljusstyrka
    analogWrite(2, i);
    // Vänta 10ms
    delay(10);
  } 
}

Arduino-projekt 001 - Blinkande lysdiod

Kopplingen

Ett mycket bra projekt att börja med om man är helt ny på Arduino är att få en lysdiod att blinka. Det första man behöver göra är att koppla in lysdioden fysiskt till Arduino-kortet. Detta görs enligt schemat nedan. Tänk på att lysdioden bara fungerar på ett håll. Det långa benet kopplas till plus (motståndet) och det korta benet kopplas till minus (GND). En lysdiod får aldrig kopplas in direkt till ett batteri eller annan strömkälla utan måste alltid ha ett lagom stort motstånd eller annan skyddsmekanism. I det här fallet använder vi ett motstånd på 220 ohm (röd-röd-svart-svart) vilket är en bra kompromiss för de flesta lysdioder kopplade till 5 volt. Vita och blåa lysdioder kan behöva ett mindre motstånd, typ 100 ohm för att lysa med full styrka, men kommer att fungera även med 220 ohm.

Arduino exempel 001 - blinkande lysdiod
Arduino exempel 001 - blinkande lysdiod

Beskrivning

Kopiera koden nedan till Arduinos utvecklingsmiljö, kompilera och ladda upp till Arduino-kortet. När kortet får ström kommer processorn att först köra setup(). Det enda som setup() gör är att tala om för processorn att vi vill att anslutning 2 ska vara en utgång. Alla digitala anslutningar på Arduino-kortet är inställda att vara ingångar vid uppstart.

Därefter tar funktionen loop() över. Denna funktion kommer att anropas om och om igen i oändligheten tills strömmen till kortet bryts eller du trycker på reset-knappen. I loop() så tänder vi först lysdioden genom att sätta anslutning 2 till hög (5 Volt). Därefter väntar vi en halv sekund (500 ms). Sedan släcker vi lysdioden igenom genom att skriva en nolla på anslutning 2 och sedan väntar vi en halv sekund igen. Efter det är programmet slut, men som vi precis påpekade så kommer loop() automatiskt att anropas igen och programmet startar alltså om från början med att tända lydioden igen. Detta leder till att lysdioden kommer att blinka en gång i sekunden.

Koden

void setup() {
  // Ställ om pinne 2 till till att vara en utgång.
  pinMode(2, OUTPUT);
}

void loop() {
  // Tänd lysdioden
  digitalWrite(2, HIGH);
  // Vänta en halv sekund (500ms)
  delay(500);
  // Släck lysdioden
  digitalWrite(2, LOW);
  // Vänta ytterligare en halv sekund innan programmet börjar om
  delay(500);
}

Alfanumerisk LCD med I²C-anslutning

Alfanumeriska LCD-displayer är väldigt populära för att visa status och meddelande från Arduinos och andra mikrokontroller. Eftersom de är populära så finns det väldigt bra kodbibliotek tillgänliga och många kodexempel på hur dessa används. Om man kombinerar en sådan här display med en I²C-modul så har man en oslagbar kombination som är enkel att koppla in och använder minimalt med anslutningar på din mikrokontroller.

Här följer vårt enkla test-program för Arduino som visar hur man kan använda just en sådan här LCD kombinerad med I2C-modul:

#include <LiquidCrystal_I2C.h>
#include <string.h>

// Adressen är 0x27, 16 tecken och två rader
LiquidCrystal_I2C lcd(0x27, 16, 2);

char *txt = "Allt f\xEFr din robot-hobby!";  // åäö ligger på udda ställe i teckentabellen
unsigned char pos = 0;
unsigned char txtlen;

void setup() {
  txtlen = strlen(txt);
  lcd.init(); // start the library
  lcd.clear();
  lcd.home();
  lcd.backlight();
  lcd.setCursor(0, 0);
  lcd.print("ByggaRobot.se");
}

void loop() {
  lcd.setCursor(0, 1);
  lcd.print(txt + pos);
  if (pos > (txtlen - 15)) {
    lcd.setCursor(txtlen - pos + 1, 1);
    lcd.print(txt);
  }
  pos++;
  if (pos > txtlen) {
    pos = 0;
  }
  delay(500);
}

Här är en film som visar resultatet av koden när den körs:

LED Matris-modul 8x8

Tack vara det kraftfulla drivchippet MAX7219 så kan varje lysdiod i den här modulen styras individuellt och ljusstyrkan kan  ställas in för alla lysdioder i 16 steg. Det finns ett färdigt kodbibliotek tillgängligt för nerladdning direkt i Arduinos kod-editor vilket gör det mycket enkelt att skriva program för att styra modulen. Här nedan följer ett enkelt kodexempel för Arduino som visar texten "BYGGAROBOT.SE" bokstav för bokstav.

#include <avr/pgmspace.h>
#include <LedControl.h>

// Visa varje bildruta i 500ms
#define FRAME_TIME 500

LedControl ledmatrix = LedControl(2, 3, 4, 1); // Data = pin 2, Clock = pin 3, Load = pin 4, Bara en LED-modul är inkopplad

// Definiera alla bildrutor.
// De lagras direkt i programminnet för att spara utrymme i dataminnet.
const char frames[][8] PROGMEM = {
  {
    B11111100,
    B11000110,
    B11000110,
    B11111000,
    B11000110,
    B11000011,
    B11000011,
    B11111100
  },
  {
    B11000011,
    B11000011,
    B01100110,
    B00111100,
    B00011000,
    B00011000,
    B00011000,
    B00011000
  },
  {
    B00111100,
    B01100110,
    B11000000,
    B11000000,
    B11001111,
    B11000011,
    B01100110,
    B00111000
  },
  {
    B00111100,
    B01100110,
    B11000000,
    B11000000,
    B11001111,
    B11000011,
    B01100110,
    B00111000
  },
  {
    B00011000,
    B00111100,
    B01100110,
    B11000011,
    B11111111,
    B11000011,
    B11000011,
    B11000011
  },
  {
    B11111100,
    B11000110,
    B11000110,
    B11111000,
    B11001100,
    B11000110,
    B11000011,
    B11000011
  },
  {
    B00111100,
    B01100110,
    B11000011,
    B11000011,
    B11000011,
    B11000011,
    B01100110,
    B00111000
  },
  {
    B11111100,
    B11000110,
    B11000110,
    B11111000,
    B11000110,
    B11000011,
    B11000011,
    B11111100
  },
  {
    B00111100,
    B01100110,
    B11000011,
    B11000011,
    B11000011,
    B11000011,
    B01100110,
    B00111000
  },
  {
    B11111111,
    B00011000,
    B00011000,
    B00011000,
    B00011000,
    B00011000,
    B00011000,
    B00011000
  },
  {
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00011000,
    B00011000
  },
  {
    B01111110,
    B11000011,
    B11000000,
    B01111100,
    B00000110,
    B00000011,
    B11000011,
    B01111110
  },
  {
    B11111110,
    B11000000,
    B11000000,
    B11111000,
    B11000000,
    B11000000,
    B11000000,
    B11111111
  },
  {
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000,
    B00000000
  }
};


void setup() {
  ledmatrix.shutdown(0, false);
  ledmatrix.setIntensity(0, 8);
  ledmatrix.clearDisplay(0);
}

void loop() {
  for (int i = 0; i < sizeof(frames) / sizeof(frames[0]); i++) {
    set_matrix(0, frames[i]);
    delay(FRAME_TIME);
  }
}

void set_matrix(int addr, const char *image) {
  for (int row = 0; row < 8; row++) {
    ledmatrix.setRow(addr, row, image[row]);
  }
}

Här är en film som visar resultatet av koden:

Kundvagn