// Arduino-programmet hedder en "sketch" og har altid
// to hovedfunktioner — ingen main() skrives selv:
const int ledPin = 13; // globale variable erklæres her
void setup()
{
// køres ÉN gang når boardet starter/reset
pinMode(ledPin, OUTPUT);
Serial.begin(9600);
}
void loop()
{
// køres igen og igen, for evigt
digitalWrite(ledPin, HIGH);
delay(1000);
digitalWrite(ledPin, LOW);
delay(1000);
}
Husk: funktionsprototyper genereres automatisk af Arduino IDE — du kan kalde funktioner før de er defineret i filen.
bool isReady = true; // true / false
byte smallValue = 255; // 0-255
char grade = 'A'; // ét tegn (ASCII)
int count = -32000; // heltal — se note!
unsigned int distance = 40000; // kun positive
long bigNumber = 123456; // stort heltal
unsigned long timestamp = millis();// altid til millis()!
float temperature = 23.5; // decimaltal (~6-7 cifre)
double precise = 3.14159; // = float på Uno
String text = "Hej"; // se faldgruber
Vigtigt: int er
16-bit på klassisk Uno (AVR, max 32767) men 32-bit på Uno R4 (ARM).
Brug long hvis du er i tvivl — og altid unsigned long til
millis().
// Erklæring + tildeling på én gang (bedst!)
int sensorValue = 0;
// const — kan ikke ændres, bruger ikke RAM
const int ledPin = 13;
// #define — tekst-udskiftning før compile (ingen type-check)
#define MAX_SPEED 255
// static i en funktion — bevarer værdien mellem kald
void measure() {
static unsigned long lastTime = millis();
}
Foretræk const frem for #define til pins og
grænser — compileren type-checker.
+ - * /plus, minus, gange, dividér (int-division kasserer resten)
%modulo — rest ved division: 7 % 3 giver 1
++ --læg 1 til / træk 1 fra: count++
+= -= *= /=regn og gem: speed += 10
== !=lig med / forskellig fra (sammenligning — ikke =!)
< > <= >=mindre, større, mindre-lig, større-lig
&& || !logisk OG, ELLER, IKKE
& | ^ ~bitvis OG, ELLER, XOR, NOT
<< >>bit-skift venstre/højre: 1 << 3 = 8
if (temperature > 25) {
digitalWrite(fanPin, HIGH);
} else if (temperature < 20) {
digitalWrite(heaterPin, HIGH);
} else {
digitalWrite(fanPin, LOW);
}
switch (command) {
case 'a':
driveForward();
break; // uden break "falder man videre"
case 'b':
brake();
break;
default:
break;
}
for (int i = 0; i < 10; i++) { // tælle-loop
Serial.println(i);
}
while (digitalRead(buttonPin) == HIGH) {
// vent til knappen trykkes
}
do {
readSensor(); // køres mindst én gang
} while (value < 100);
int add(int a, int b) // returnerer en værdi
{
return a + b;
}
void blinkLed(int times) // void = ingen returværdi
{
for (int i = 0; i < times; i++) {
digitalWrite(ledPin, HIGH);
delay(200);
digitalWrite(ledPin, LOW);
delay(200);
}
}
// & = reference: funktionen kan ændre kalderens variable
bool readSensor(float &temperature, float &humidity)
{
temperature = 23.5; // ændrer variablen hos kalderen
humidity = 45.0;
return true; // fortæl om det lykkedes
}
// kald:
float temp, hum;
if (readSensor(temp, hum)) {
Serial.println(temp);
}
int readings[10]; // array af 10 int
readings[0] = analogRead(A0); // 0-indekseret!
int pins[] = {3, 5, 6}; // initialiseret array
int pinCount = sizeof(pins) / sizeof(pins[0]);
char name[] = "Anna"; // char-array: 5 bytes inkl. '\0'
String greeting = "Hej " + String(name);
Serial.println(F("Fast tekst")); // F() = tekst i flash, sparer RAM
RAM er lille: brug F() omkring faste tekster i
Serial.print — ellers kopieres de til RAM ved opstart.
const int ledPin = 13;
pinMode(ledPin, OUTPUT); // i setup()
digitalWrite(ledPin, HIGH); // tænd (5V / 3.3V)
digitalWrite(ledPin, LOW); // sluk (0V)
// RGB-LED: én PWM-pin pr. farve — bland farver med analogWrite
analogWrite(redPin, 255);
analogWrite(greenPin, 0);
analogWrite(bluePin, 128); // lilla
// Relæ og aktiv buzzer styres som en LED:
digitalWrite(relayPin, HIGH); // relæ trækker
digitalWrite(buzzerPin, HIGH); // aktiv buzzer bipper
Vigtigt: Husk altid
pinMode() i setup() før brug. LED kræver
modstand (typisk 220 Ω) i serie.
const int buttonPin = 2;
pinMode(buttonPin, INPUT_PULLUP); // intern pull-up modstand
if (digitalRead(buttonPin) == LOW) {
// knappen er trykket ned!
}
// PIR-bevægelsessensor (HC-SR501): HIGH ved bevægelse
pinMode(pirPin, INPUT);
if (digitalRead(pirPin) == HIGH) {
// bevægelse registreret
}
Vigtigt: Med INPUT_PULLUP er
logikken omvendt: trykket knap giver LOW (forbundet til GND),
sluppet knap giver HIGH. Ingen ekstern modstand nødvendig.
int rawValue = analogRead(A0); // 0-1023 (10-bit)
float voltage = rawValue * (5.0 / 1023.0);
// Joystick: to analoge akser + digital knap
int xValue = analogRead(A0);
int yValue = analogRead(A1);
bool pressed = digitalRead(joyButtonPin) == LOW;
Analoge pins (A0-A5) behøver ikke pinMode().
På Uno R4 kan opløsningen hæves: analogReadResolution(14)
giver 0-16383.
analogWrite(ledPin, 128); // 0-255: 0=slukket, 255=fuld styrke
// Omskaler analog læsning (0-1023) til PWM (0-255):
int speed = map(joystickValue, 0, 1023, 0, 255);
speed = constrain(speed, 0, 255); // hold inden for grænser
analogWrite(motorPin, speed);
Vigtigt: analogWrite() (PWM)
virker kun på pins mærket ~: 3, 5, 6, 9, 10, 11.
PWM blinker så hurtigt at øjet/motoren ser et gennemsnit.
delay(1000); // vent 1 sekund — BLOKERER alt!
delayMicroseconds(10); // til præcise korte pulser
// Non-blocking mønster: gør noget med mellemrum uden at stoppe
unsigned long previousMillis = 0;
const long interval = 1000;
void loop() {
unsigned long now = millis();
if (now - previousMillis >= interval) {
previousMillis = now;
// gør noget hvert sekund — resten af loop() kører videre
}
}
millis() returnerer ms siden opstart
(unsigned long, overflow efter ca. 49 dage — subtraktions-mønsteret
ovenfor overlever det). micros() giver mikrosekunder.
Serial.begin(9600); // i setup() — 115200 er hurtigere
Serial.print("Temp: ");
Serial.print(temperature, 1); // float med 1 decimal
Serial.println(" C"); // println = linjeskift til sidst
// Læs kommandoer fra brugeren:
if (Serial.available() > 0) {
char command = Serial.read(); // ét tegn ad gangen
if (command == 'a') {
digitalWrite(ledPin, HIGH);
}
}
// hele tal: int value = Serial.parseInt();
// hele linjer: String line = Serial.readStringUntil('\n');
Vigtigt: Baud-raten i
Serial.begin() skal matche indstillingen i Serial Monitor
(9600 eller 115200) — ellers vises volapyk.
volatile bool motionDetected = false; // volatile = delt med ISR
void onMotion() // ISR: Interrupt Service Routine
{
motionDetected = true; // hold den KORT — ingen delay()/Serial her!
}
void setup()
{
pinMode(2, INPUT_PULLUP);
attachInterrupt(digitalPinToInterrupt(2), onMotion, FALLING);
// modes: RISING, FALLING, CHANGE, LOW
}
Klassisk Uno: kun pin 2 og 3. Uno R4: næsten alle pins.
Delte variable skal være volatile — ellers kan compileren optimere
læsningen væk.
#include "pitches.h" // NOTE_C4 osv. — egen fil i sketch-mappen
tone(8, NOTE_C4, 500); // pin, frekvens (Hz), varighed (ms)
delay(500);
noTone(8); // stop lyden
// Melodi som to parallelle arrays:
int melody[] = {NOTE_C4, NOTE_E4, NOTE_G4};
int durations[] = {500, 500, 1000};
for (int i = 0; i < 3; i++) {
tone(8, melody[i], durations[i]);
delay(durations[i]);
}
Passiv buzzer = tone() med valgfri frekvens.
Aktiv buzzer = kun digitalWrite(HIGH) — én fast tone.
const int dataPin = 8; // DS — data
const int clockPin = 9; // SH_CP — clock
const int latchPin = 10; // ST_CP — latch
pinMode(dataPin, OUTPUT);
pinMode(clockPin, OUTPUT);
pinMode(latchPin, OUTPUT);
digitalWrite(latchPin, LOW);
shiftOut(dataPin, clockPin, MSBFIRST, 0b10101010);
digitalWrite(latchPin, HIGH); // først NU opdateres output
Ét byte styrer 8 udgange — kun 3 pins brugt. Chips kan kaskades
(2 chips = 16 udgange = 2 shiftOut()). Bruges til 8 LED,
7-segment og 4-cifret display.
#include <Servo.h>
Servo myServo; // opret servo-objekt
void setup()
{
myServo.attach(9); // hvilken pin (PWM)
}
void loop()
{
myServo.write(0); // vinkel 0-180 grader
delay(1000);
myServo.write(90);
delay(1000);
myServo.write(180);
delay(1000);
}
// finere styring: myServo.writeMicroseconds(1500); // 500-2500
// læs position: int angle = myServo.read();
// frigør pin: myServo.detach();
Vigtigt: Servoer og motorer trækker meget strøm — brug ekstern strømforsyning med fælles GND, aldrig boardets 5V-pin direkte.
#include <Stepper.h>
const int stepsPerRevolution = 2048; // ét helt gear-omdrejning
// pin-rækkefølgen følger driver-chippens IN1, IN3, IN2, IN4:
Stepper myStepper(stepsPerRevolution, 8, 10, 9, 11);
void setup()
{
myStepper.setSpeed(15); // omdrejninger/min (max ca. 17)
Serial.begin(9600);
}
void loop()
{
Serial.println("clockwise");
myStepper.step(stepsPerRevolution); // én omdrejning fremad
delay(500);
myStepper.step(-512); // kvart omdrejning baglæns
delay(500);
}
step() blokerer mens motoren drejer.
Kan fjernstyres: kombinér med IR (se afsnit 27).
const int in1Pin = 7; // retning (via driver/transistor)
const int in2Pin = 8; // retning
const int speedPin = 9; // PWM-hastighed (enable-pin)
void setup()
{
pinMode(in1Pin, OUTPUT);
pinMode(in2Pin, OUTPUT);
pinMode(speedPin, OUTPUT);
}
void loop()
{
digitalWrite(in1Pin, HIGH); // fremad
digitalWrite(in2Pin, LOW);
analogWrite(speedPin, 200); // 0-255
delay(2000);
digitalWrite(in1Pin, LOW); // baglæns
digitalWrite(in2Pin, HIGH);
delay(2000);
}
Vigtigt: En DC-motor må aldrig køres direkte fra en pin — brug driver-chip (L293D/L298N) eller transistor med beskyttelsesdiode.
#include <LiquidCrystal.h>
// rs, en, d4, d5, d6, d7
LiquidCrystal lcd(7, 8, 9, 10, 11, 12);
void setup()
{
lcd.begin(16, 2); // 16 tegn, 2 linjer
}
void loop()
{
lcd.setCursor(0, 0); // kolonne, række (0-indekseret)
lcd.print("Temp: ");
lcd.print(temperature, 1);
lcd.setCursor(0, 1);
lcd.print("Fugt: ");
lcd.print(humidity, 0);
delay(2000);
}
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
Adafruit_SSD1306 display(128, 64, &Wire, -1);
void setup()
{
display.begin(SSD1306_SWITCHCAPVCC, 0x3C); // I2C-adresse
display.clearDisplay();
display.setTextSize(1); // 1 = lille, 2 = dobbelt
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("Hej verden");
display.drawPixel(10, 20, SSD1306_WHITE);
display.display(); // intet vises før display() kaldes!
}
Tilsluttes SDA/SCL (I2C). Tegn alt i hukommelsen først —
display() sender billedet til skærmen.
#include <Wire.h>
const int mpuAddress = 0x68; // MPU6050's I2C-adresse
void setup()
{
Wire.begin(); // start I2C-bus
// væk sensoren (power management-register):
Wire.beginTransmission(mpuAddress);
Wire.write(0x6B); // register
Wire.write(0); // værdi
Wire.endTransmission(true);
}
void loop()
{
Wire.beginTransmission(mpuAddress);
Wire.write(0x3B); // første accelerometer-register
Wire.endTransmission(false); // gentaget start
Wire.requestFrom(mpuAddress, 6, true);
// to bytes samles til ét 16-bit tal:
int16_t accX = Wire.read() << 8 | Wire.read();
int16_t accY = Wire.read() << 8 | Wire.read();
int16_t accZ = Wire.read() << 8 | Wire.read();
delay(500);
}
I2C-mønsteret: beginTransmission →
write (register) → endTransmission →
requestFrom → read.
#include <SPI.h>
#include <MFRC522.h>
#define SS_PIN 10
#define RST_PIN 9
MFRC522 mfrc522(SS_PIN, RST_PIN);
void setup()
{
Serial.begin(9600);
SPI.begin(); // start SPI-bus (pins 11-13)
mfrc522.PCD_Init(); // start RFID-læseren
}
void loop()
{
if (!mfrc522.PICC_IsNewCardPresent()) return;
if (!mfrc522.PICC_ReadCardSerial()) return;
for (byte i = 0; i < mfrc522.uid.size; i++) {
Serial.print(mfrc522.uid.uidByte[i], HEX);
Serial.print(" ");
}
Serial.println();
mfrc522.PICC_HaltA(); // færdig med kortet
}
const int trigPin = 12;
const int echoPin = 13;
pinMode(trigPin, OUTPUT);
pinMode(echoPin, INPUT);
// send en 10 µs lydpuls:
digitalWrite(trigPin, LOW);
delayMicroseconds(2);
digitalWrite(trigPin, HIGH);
delayMicroseconds(10);
digitalWrite(trigPin, LOW);
// mål hvor lang tid ekkoet er om at vende tilbage:
long duration = pulseIn(echoPin, HIGH);
float distanceCm = duration * 0.034 / 2; // lydens hastighed
Alternativ med bibliotek: #include "SR04.h" —
SR04 sr04 = SR04(echoPin, trigPin); og
float cm = sr04.GetDistance();
#include <DHT.h>
#define DHT_PIN 8
#define DHT_TYPE DHT11
DHT dht(DHT_PIN, DHT_TYPE);
void setup()
{
Serial.begin(9600);
dht.begin();
}
void loop()
{
float humidity = dht.readHumidity();
float temperature = dht.readTemperature();
if (isnan(temperature) || isnan(humidity)) {
Serial.println("Måling fejlede!");
return;
}
Serial.print(temperature, 1);
Serial.print(" C, ");
Serial.print(humidity, 1);
Serial.println(" %");
delay(2000);
}
Vigtigt: DHT11 kan kun måle én gang
pr. ca. 2 sekund — brug millis()-mønsteret (afsnit 12), hvis
resten af programmet ikke må vente.
#include <Keypad.h>
const byte ROWS = 4;
const byte COLS = 4;
char keys[ROWS][COLS] = {
{'1', '2', '3', 'A'},
{'4', '5', '6', 'B'},
{'7', '8', '9', 'C'},
{'*', '0', '#', 'D'}
};
byte rowPins[ROWS] = {9, 8, 7, 6};
byte colPins[COLS] = {5, 4, 3, 2};
Keypad keypad = Keypad(makeKeymap(keys), rowPins, colPins, ROWS, COLS);
void loop()
{
char key = keypad.getKey();
if (key) { // ikke NO_KEY
Serial.println(key);
}
}
#include <IRremote.h>
const int receiverPin = 11;
IRrecv irrecv(receiverPin);
decode_results results;
void setup()
{
Serial.begin(9600);
irrecv.enableIRIn(); // start IR-modtageren
}
void loop()
{
if (irrecv.decode(&results)) {
Serial.println(results.value, HEX); // knappens kode
irrecv.resume(); // klar til næste
}
}
Nyere IRremote (v3+) bruger i stedet
IrReceiver.begin(receiverPin),
IrReceiver.decode() og
IrReceiver.decodedIRData.command.
#include "Arduino_LED_Matrix.h"
ArduinoLEDMatrix matrix;
// 8x12 = 96 LED pakket i 3 x 32-bit (lavet i LED Matrix Editor):
const uint32_t smiley[] = {
0x19819,
0x80000001,
0x81f8000
};
void setup()
{
matrix.begin();
matrix.loadFrame(smiley); // vis ét billede
}
Mønstre designes i Arduino's online LED Matrix Editor
og indsættes som const uint32_t-arrays.
#include "RTC.h"
void setup()
{
RTC.begin();
RTCTime startTime(1, Month::JANUARY, 2025, 12, 0, 0,
DayOfWeek::WEDNESDAY, SaveLight::SAVING_TIME_ACTIVE);
RTC.setRTCTime(startTime); // stil uret
}
void loop()
{
RTCTime now;
RTC.getTime(now); // læs uret
Serial.print(now.getHour());
Serial.print(":");
Serial.println(now.getMinutes());
delay(1000);
}
#include "CapacitiveTouch.h"
CapacitiveTouch touchPad(4); // berørings-pin
void setup()
{
Serial.begin(9600);
touchPad.begin();
}
void loop()
{
int value = touchPad.read(); // rå værdi — stiger ved berøring
Serial.println(value);
if (value > 1000) { // find grænsen ved at aflæse
digitalWrite(ledPin, HIGH);
}
delay(100);
}
#include <WiFiS3.h>
char ssid[] = "MitNetvaerk"; // netværkets navn
char pass[] = "MinKode"; // kun 2.4 GHz — ikke 5 GHz!
void setup()
{
Serial.begin(9600);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println(WiFi.localIP()); // boardets IP
Serial.println(WiFi.RSSI()); // signalstyrke (dBm)
}
Fast IP (undgår DHCP-problemer / 0.0.0.0): kald
WiFi.config(ip, gateway, subnet) med
IPAddress ip(192, 168, 1, 166) før
WiFi.begin().
#include <WiFiS3.h>
char apSsid[] = "UNO-R4";
char apPass[] = "12345678";
WiFiServer server(80); // TCP-server på HTTP-porten
void setup()
{
pinMode(ledPin, OUTPUT);
WiFi.beginAP(apSsid, apPass); // boardet laver sit eget netværk
// standard-IP i AP-mode: 192.168.4.1
server.begin();
}
void loop()
{
WiFiClient client = server.accept();
if (client) {
String request = client.readStringUntil('\r');
if (request.indexOf("led=on") > 0) digitalWrite(ledPin, HIGH);
if (request.indexOf("led=off") > 0) digitalWrite(ledPin, LOW);
client.println("HTTP/1.1 200 OK");
client.println("Content-Type: text/html");
client.println(); // tom linje = header slut
client.println("<h1>Hej fra UNO R4</h1>");
client.stop();
}
}
STA = boardet kobler på et eksisterende netværk. AP = boardet er selv netværket (mobilen forbinder direkte).
#include <WiFiS3.h>
#include <WiFiUdp.h>
#include <NTPClient.h>
WiFiUDP udp;
NTPClient timeClient(udp, "pool.ntp.org", 3600); // UTC+1 (DK vinter)
void setup()
{
WiFi.begin(ssid, pass); // se afsnit 31
timeClient.begin();
}
void loop()
{
timeClient.update();
Serial.println(timeClient.getFormattedTime()); // "13:45:00"
delay(1000);
}
#include <ArduinoBLE.h>
BLEService ledService("19B10000-E8F2-537E-4F6C-D104768A1214");
BLEByteCharacteristic ledChar(
"19B10001-E8F2-537E-4F6C-D104768A1214", BLERead | BLEWrite);
void setup()
{
pinMode(ledPin, OUTPUT);
BLE.begin();
BLE.setLocalName("UNO-R4");
BLE.setAdvertisedService(ledService);
ledService.addCharacteristic(ledChar);
BLE.addService(ledService);
BLE.advertise(); // bliv synlig for telefonen
}
void loop()
{
BLEDevice central = BLE.central();
if (central && central.connected()) {
if (ledChar.written()) { // telefonen har skrevet en værdi
digitalWrite(ledPin, ledChar.value() ? HIGH : LOW);
}
}
}
Styres fra mobilen med apps som LightBlue eller nRF Connect. UUID'er er unikke id'er du selv genererer.
Kapsl hardwaren ind i et objekt: pins og tilstand er private,
adgang sker via metoder. Navne i camelCase som i C#.
class Car
{
private:
int leftPin;
int rightPin;
int speedPin;
int speed;
public:
// constructor: kaldes når objektet oprettes
Car(int left, int right, int speedControl)
{
leftPin = left;
rightPin = right;
speedPin = speedControl;
speed = 0;
}
void begin() // kaldes fra setup()
{
pinMode(leftPin, OUTPUT);
pinMode(rightPin, OUTPUT);
pinMode(speedPin, OUTPUT);
brake();
}
void driveForward()
{
digitalWrite(leftPin, HIGH);
digitalWrite(rightPin, LOW);
analogWrite(speedPin, speed);
}
void driveBackward()
{
digitalWrite(leftPin, LOW);
digitalWrite(rightPin, HIGH);
analogWrite(speedPin, speed);
}
void setSpeed(int newSpeed)
{
speed = constrain(newSpeed, 0, 255);
analogWrite(speedPin, speed);
}
int getSpeed()
{
return speed;
}
void brake()
{
digitalWrite(leftPin, LOW);
digitalWrite(rightPin, LOW);
analogWrite(speedPin, 0);
}
};
Car myCar(7, 8, 9); // opret objektet (globalt)
void setup()
{
myCar.begin();
myCar.setSpeed(200);
}
void loop()
{
myCar.driveForward();
delay(2000);
myCar.brake();
delay(1000);
}
Større projekter: klassen deles i Car.h
(erklæringer) og Car.cpp (kode), præcis som i almindelig C++.
pinMode() i setup() før brug af en pinINPUT_PULLUP: trykket knap giver LOW (omvendt logik)delay() blokerer hele programmet — brug millis()-mønsteretint er 16-bit på klassisk Uno, 32-bit på Uno R4 — brug
unsigned long til millis()String-klassen fragmenterer den lille RAM — foretræk
char[] og F("tekst")analogWrite() virker kun på PWM-pins mærket ~ (3, 5, 6, 9, 10, 11)Serial.begin() skal matche Serial Monitordelay()/Serial,
brug volatile på delte variablefloat til int: decimaler smides væk (ingen afrunding)pinModesæt pin som OUTPUT, INPUT eller INPUT_PULLUP
digitalWriteskriv HIGH/LOW til en digital pin
digitalReadlæs HIGH/LOW fra en digital pin
analogReadlæs analog pin, 0-1023 (10-bit)
analogWritePWM-output 0-255 på ~ pins
pulseInmål længden af en puls i µs
shiftOutsend et byte bit for bit (74HC595)
tone / noTonefrekvens på pin / stop lyd
delayvent i millisekunder (blokerer)
delayMicrosecondsvent i mikrosekunder
millisms siden opstart (unsigned long)
microsµs siden opstart
mapomskaler fra ét interval til et andet
constrainhold værdi inden for min/max
min / max / absmindste, største, absolut værdi
random / randomSeedtilfældigt tal / start tilfældighed
bitSet / bitClear / bitReadsæt, ryd, læs én bit
lowByte / highBytelaveste/højeste byte af et word
Serial.beginstart seriel forbindelse (baud-rate)
Serial.print / printlnskriv til monitor (println = linjeskift)
Serial.availableantal tegn klar til at læses
Serial.read / parseIntlæs tegn / læs heltal
attachInterruptkald en funktion ved pin-ændring
digitalPinToInterruptoversæt pin-nummer til interrupt