ARDUINO • PROGRAMMAZIONE • C/C++

Programmazione Arduino: guida completa agli sketch

Una guida pratica per imparare a programmare Arduino partendo dalle basi: struttura di uno sketch, variabili, condizioni, cicli, operatori, input e output, comunicazione seriale, gestione del tempo e principali funzioni del linguaggio.

🎯 Da dove iniziare?

Non è necessario imparare tutto a memoria. Il modo migliore per apprendere la programmazione Arduino è leggere i concetti fondamentali e metterli subito in pratica modificando piccoli sketch già funzionanti.

🧠 Livello Da principiante a intermedio.
💻 Linguaggio C/C++ con API e funzioni Arduino.
🔧 Obiettivo Imparare a leggere, modificare e scrivere sketch autonomamente.

🧠 Introduzione alla programmazione Arduino

Un programma Arduino viene comunemente chiamato sketch.

Lo sketch contiene tutte le istruzioni che il microcontrollore deve eseguire per leggere sensori, prendere decisioni e controllare dispositivi come LED, relè, motori, display e moduli di comunicazione.

Arduino utilizza un ambiente di programmazione basato sul C/C++, arricchito da funzioni e librerie che rendono molto più semplice l’utilizzo dell’hardware.

ℹ️ Wiring e Arduino

Nelle vecchie guide si trova spesso il termine Wiring per indicare il linguaggio di Arduino. Storicamente Wiring ha avuto un ruolo importante nello sviluppo dell’ambiente Arduino, ma oggi per un tutorial moderno è più corretto parlare di C/C++ e delle API Arduino.

Per scrivere e caricare gli sketch è possibile utilizzare Arduino IDE 2.

Nell’IDE possiamo:

  • scrivere il codice;
  • controllarne la sintassi;
  • compilarlo;
  • caricarlo sulla scheda;
  • visualizzare dati tramite Monitor Seriale;
  • installare librerie e supporto per nuove schede.

Puoi approfondire l’uso dell’ambiente di sviluppo nella nostra guida:

👉 Guida ad Arduino IDE e agli sketch

💡 Consiglio per chi inizia

Parti da programmi molto semplici, come Blink. Leggi il codice, caricalo sulla scheda, modifica un valore, osserva cosa cambia e ripeti l’esperimento. È molto più efficace della sola memorizzazione teorica.


🧩 Come si progetta un programma

Un programma è una sequenza di istruzioni che il microcontrollore esegue secondo una logica precisa.

Prima di iniziare a scrivere codice è utile dividere il problema in piccoli blocchi.

1. Acquisizione dei dati

Determina quali informazioni devono essere lette.

Ad esempio:

  • temperatura;
  • distanza;
  • pressione di un pulsante;
  • luminosità;
  • movimento.

2. Elaborazione e decisione

Una volta ottenuti i dati bisogna stabilire cosa deve fare Arduino.

Ad esempio:

if (temperatura > 30) {
    // attiva la ventola
}

3. Controllo degli attuatori

Infine Arduino può agire su uno o più dispositivi:

  • LED;
  • buzzer;
  • motori;
  • servo;
  • relè;
  • display.
Esempio schema Arduino per programmazione e sensori

Esempio: misurare una distanza

Consideriamo un sensore HC-SR04 con:

  • TRIG collegato al pin 2;
  • ECHO collegato al pin 3;
  • VCC a 5V;
  • GND a GND.
float cm = 0;

void misurazioneDistanza() {

    digitalWrite(2, LOW);
    delayMicroseconds(2);

    digitalWrite(2, HIGH);
    delayMicroseconds(10);
    digitalWrite(2, LOW);

    cm = pulseIn(3, HIGH) / 58.0;
}

void comunicazioneDistanza() {

    Serial.print(cm);
    Serial.println(" cm");
}

void setup() {

    Serial.begin(9600);

    pinMode(2, OUTPUT);
    pinMode(3, INPUT);
}

void loop() {

    misurazioneDistanza();
    comunicazioneDistanza();

    delay(2000);
}
🔎 Cosa osservare nel codice

Anche senza conoscere ogni istruzione, possiamo già riconoscere:

  • una variabile;
  • due funzioni personalizzate;
  • setup();
  • loop();
  • commenti;
  • parentesi graffe che delimitano i blocchi.

🏗️ Struttura di uno sketch Arduino

Quasi ogni programma Arduino contiene almeno due funzioni fondamentali:

  • setup()
  • loop()
// Dichiarazioni globali

void setup() {

    // istruzioni eseguite una sola volta

}

void loop() {

    // istruzioni ripetute continuamente

}

void setup()

Viene eseguito una sola volta, quando Arduino viene acceso o resettato.

Viene normalmente utilizzato per:

  • configurare i pin;
  • avviare la comunicazione seriale;
  • inizializzare display e sensori;
  • impostare lo stato iniziale del sistema.

void loop()

Dopo aver completato setup(), Arduino esegue loop() continuamente finché la scheda rimane alimentata.


✍️ Ortografia, grammatica e sintassi

Il compilatore richiede una sintassi precisa. Anche un piccolo errore può impedire la compilazione.

Punto e virgola

Molte istruzioni terminano con:

;

Esempio:

int led = 13;

digitalWrite(led, HIGH);

Parentesi graffe

Le parentesi:

{
}

delimitano un blocco di istruzioni.

Commenti

Commento su una sola riga:

// questo è un commento

Commento su più righe:

/*
Questo è
un commento
multiriga
*/
✅ Buona pratica

Usa l’indentazione e i commenti. Un programma ben formattato è molto più facile da leggere, correggere e modificare anche dopo mesi.


🔑 Parole chiave fondamentali

Parola Significato
HIGH Livello logico alto.
LOW Livello logico basso.
INPUT Configura un pin come ingresso.
OUTPUT Configura un pin come uscita.
INPUT_PULLUP Ingresso con resistenza pull-up interna attiva.
true Valore booleano vero.
false Valore booleano falso.

📦 Variabili

Una variabile è un’area di memoria utilizzata per memorizzare un valore.

Esempio:

int temperatura = 25;

In questo caso:

  • int è il tipo;
  • temperatura è il nome;
  • 25 è il valore iniziale.

Tipi di dato comuni

Tipo Utilizzo tipico
bool Vero/falso.
byte Interi piccoli senza segno.
char Caratteri.
int Numeri interi.
unsigned int Interi senza segno.
long Interi più grandi.
unsigned long Valori positivi grandi, molto usato con millis().
float Numeri con parte decimale.
⚠️ La dimensione dei tipi dipende dalla piattaforma

Le dimensioni di alcuni tipi, come int, possono cambiare passando da Arduino UNO ad altre famiglie di microcontrollori. Evita quindi di considerare universali le dimensioni tipiche dell’ATmega328P.

Variabili globali e locali

Una variabile dichiarata fuori dalle funzioni può essere utilizzata in più parti dello sketch.

int led = 13;

void setup() {
}

void loop() {
}

Una variabile dichiarata dentro una funzione è invece normalmente visibile solo all’interno del relativo blocco.

Array

Un array raccoglie più elementi dello stesso tipo.

int valori[4] = {10, 25, 50, 100};

L’indice parte da 0.

  • valori[0] → 10
  • valori[1] → 25
  • valori[2] → 50
  • valori[3] → 100

#define e const

Possiamo creare una costante simbolica con:

#define LED_PIN 5

In molti casi moderni è però preferibile utilizzare una costante tipizzata:

const int LED_PIN = 5;

🔀 Istruzioni di controllo

if

Esegue un blocco soltanto se una condizione è vera.

if (temperatura > 30) {

    digitalWrite(ventola, HIGH);

}

if…else

if (temperatura > 30) {

    digitalWrite(ventola, HIGH);

}
else {

    digitalWrite(ventola, LOW);

}
⚠️ = e == non sono la stessa cosa

a = 10; assegna il valore 10.

a == 10 verifica se a contiene il valore 10.

for

Il ciclo for è utile quando sappiamo quante volte vogliamo ripetere un’operazione.

for (int i = 0; i < 10; i++) {

    Serial.println(i);

}

while

Ripete il blocco finché la condizione rimane vera.

while (sensore < 500) {

    sensore = analogRead(A0);

}

do…while

È simile a while, ma il blocco viene eseguito almeno una volta.

do {

    sensore = analogRead(A0);

} while (sensore < 500);

switch…case

È utile quando dobbiamo scegliere tra diversi casi in base allo stesso valore.

switch (modalita) {

    case 1:
        digitalWrite(LED_BUILTIN, HIGH);
        break;

    case 2:
        digitalWrite(LED_BUILTIN, LOW);
        break;

    default:
        break;
}

break

Interrompe un ciclo oppure termina un ramo di uno switch.

continue

Salta il resto dell’iterazione corrente e passa alla successiva.


➗ Operatori

Operatori matematici

Operatore Operazione
+Addizione
-Sottrazione
*Moltiplicazione
/Divisione
%Resto della divisione

Operatori di confronto

  • == uguale
  • != diverso
  • < minore
  • > maggiore
  • <= minore o uguale
  • >= maggiore o uguale

Operatori booleani

  • && AND
  • || OR
  • ! NOT

Esempio:

if (temperatura > 30 && umidita > 70) {

    // entrambe le condizioni sono vere

}

Operatori di incremento

i++;
i--;
i += 5;
i -= 5;

🧩 Librerie e funzioni

Una funzione è un blocco di codice che svolge un compito specifico.

Arduino mette a disposizione numerose funzioni già pronte, ad esempio:

  • digitalWrite()
  • digitalRead()
  • analogRead()
  • delay()
  • millis()

Possiamo anche creare funzioni personalizzate.

void accendiLed() {

    digitalWrite(LED_BUILTIN, HIGH);

}

e richiamarle con:

accendiLed();

Librerie

Le librerie aggiungono funzionalità allo sketch.

Esempio:

#include <Servo.h>

Con una libreria possiamo controllare componenti complessi senza dover riscrivere da zero tutto il protocollo di comunicazione.


🔌 Funzioni di INPUT e OUTPUT

pinMode()

Configura il funzionamento di un pin.

pinMode(8, OUTPUT);
pinMode(2, INPUT);

digitalWrite()

Imposta un’uscita digitale.

digitalWrite(8, HIGH);
digitalWrite(8, LOW);

digitalRead()

Legge lo stato di un ingresso digitale.

int stato = digitalRead(2);

analogRead()

Legge un ingresso analogico.

Su Arduino UNO R3 con ATmega328P il convertitore ADC è a 10 bit e produce normalmente valori da:

0 a 1023

int valore = analogRead(A0);

analogWrite()

Sulle classiche schede AVR come Arduino UNO, analogWrite() viene normalmente utilizzato per generare PWM.

analogWrite(9, 128);

Un valore di 128 corrisponde indicativamente a un duty cycle del 50%.

⚠️ Non tutte le schede Arduino sono uguali

Risoluzione ADC, pin PWM e comportamento di alcune funzioni possono cambiare tra UNO R3, UNO R4, Nano, Mega, ESP32 e altre piattaforme. Controlla sempre la documentazione della scheda utilizzata.


💬 Comunicazione seriale

La comunicazione seriale permette lo scambio di dati tra Arduino e il computer.

Serial.begin()

Serial.begin(9600);

Imposta la velocità della comunicazione.

Serial.print()

Serial.print("Temperatura: ");
Serial.print(temperatura);

Serial.println()

Funziona come Serial.print(), ma termina la riga.

Serial.println("Arduino avviato");

Serial.available()

Verifica se sono disponibili dati ricevuti.

if (Serial.available() > 0) {

    char dato = Serial.read();

}

Serial.read()

Legge un byte ricevuto.

char comando = Serial.read();

⏱️ Funzioni di tempo

delay()

Ferma l’esecuzione dello sketch per il numero indicato di millisecondi.

delay(1000);

equivale a una pausa di un secondo.

⚠️ delay() è bloccante

Durante il delay il normale flusso dello sketch rimane fermo. Nei progetti più complessi è spesso preferibile usare millis().

delayMicroseconds()

delayMicroseconds(10);

Introduce una pausa espressa in microsecondi.

millis()

Restituisce il numero di millisecondi trascorsi dall’avvio dello sketch.

unsigned long adesso = millis();

Esempio senza delay()

const int ledPin = LED_BUILTIN;

unsigned long precedente = 0;
const unsigned long intervallo = 1000;

bool statoLed = false;

void setup() {

    pinMode(ledPin, OUTPUT);

}

void loop() {

    unsigned long adesso = millis();

    if (adesso - precedente >= intervallo) {

        precedente = adesso;

        statoLed = !statoLed;

        digitalWrite(ledPin, statoLed);

    }
}
💡 Perché millis() è importante?

Permette di eseguire operazioni periodiche senza bloccare completamente lo sketch. È uno dei concetti più importanti per passare dai progetti base a programmi Arduino più evoluti.


📐 Funzioni matematiche

Arduino mette a disposizione diverse funzioni utili.

min()

int minimo = min(a, b);

max()

int massimo = max(a, b);

abs()

int valore = abs(numero);

constrain()

Limita un valore entro un intervallo.

valore = constrain(valore, 0, 255);

map()

Converte un valore da un intervallo a un altro.

int pwm = map(sensore, 0, 1023, 0, 255);

È molto utile per trasformare una lettura analogica in un valore PWM.

pow() e sqrt()

float quadrato = pow(numero, 2);
float radice = sqrt(numero);

🎲 Numeri casuali

random()

Genera valori pseudo-casuali.

long numero = random(0, 10);

Il risultato può andare da 0 a 9.

randomSeed()

Permette di inizializzare il generatore pseudo-casuale.

randomSeed(analogRead(A0));

Questa tecnica viene spesso utilizzata con un ingresso analogico lasciato non collegato, anche se non rappresenta una sorgente casuale crittograficamente sicura.


🧠 Alcune buone pratiche

  • usa nomi di variabili comprensibili;
  • indenta correttamente il codice;
  • commenta i passaggi non evidenti;
  • evita numeri “magici” ripetuti nel codice;
  • dividi programmi lunghi in funzioni;
  • controlla sempre i valori restituiti dai sensori;
  • usa millis() quando non vuoi bloccare lo sketch;
  • mantieni separata la logica di lettura, decisione e azione.
🧩 Un programma complesso nasce da programmi semplici

Un sistema domotico, un robot o una stazione meteo non sono altro che molti piccoli problemi risolti separatamente e poi collegati tra loro.


🛠️ Ambiente di sviluppo

Arduino IDE permette di scrivere, compilare e caricare gli sketch sulla scheda.

🏗️ Struttura

Le due funzioni fondamentali sono:

  • setup() → eseguita una volta;
  • loop() → ripetuta continuamente.

📦 Variabili

Conservano i dati utilizzati dal programma.

Tra i tipi più comuni troviamo:

  • bool;
  • byte;
  • int;
  • long;
  • float;
  • char.

🔀 Controllo del programma

  • if / else
  • for
  • while
  • do...while
  • switch / case
  • break
  • continue

⚡ Input e output

  • pinMode()
  • digitalWrite()
  • digitalRead()
  • analogRead()
  • analogWrite()

💬 Comunicazione seriale

  • Serial.begin()
  • Serial.print()
  • Serial.println()
  • Serial.available()
  • Serial.read()

⏱️ Tempo

  • delay()
  • delayMicroseconds()
  • millis()
🎯 Il concetto più importante

Non cercare di memorizzare tutte le istruzioni. Impara invece a capire la logica di un programma, a leggere la documentazione e a dividere un problema complesso in piccoli blocchi. Quando questo meccanismo diventa naturale, programmare Arduino diventa molto più semplice.


🚀 Cosa studiare dopo

Dopo aver compreso questi concetti puoi passare a progetti sempre più avanzati:

  • sensori analogici e digitali;
  • display LCD e OLED;
  • servo e motori DC;
  • interrupt;
  • I2C, SPI e UART;
  • Bluetooth;
  • WiFi e IoT;
  • ESP32.

La programmazione diventa davvero naturale quando la teoria viene applicata continuamente a circuiti e progetti reali.