Basi · 16 min di lettura

Comandi Arduino: elenco con sintassi, esempi ed errori comuni

Comandi Arduino spiegati: pinMode, digitalWrite, analogRead, analogWrite, millis, Serial, map, tone, pulseIn e attachInterrupt con esempi ed errori comuni.

I comandi Arduino sono funzioni già pronte che dicono alla scheda cosa fare: pinMode() trasforma un pin in uscita, digitalWrite() accende un LED, analogRead() legge un sensore e Serial.println() invia un messaggio al computer. La maggior parte degli sketch Arduino non è altro che una ventina di comandi di base combinati in modi diversi. Quando hai capito bene questi comandi e il loro significato, puoi leggere qualsiasi codice, dal LED che lampeggia alla macchina robot, e scrivere il tuo progetto da zero.

In questa guida abbiamo raccolto i comandi Arduino più usati in quattro gruppi: ingresso e uscita, tempo, matematica e comunicazione. Per ogni comando trovi la sintassi, il significato dei parametri, un breve esempio che si compila e funziona su un Arduino Uno e l'errore che i principianti fanno più spesso. Nelle ultime sezioni vedremo anche come gli stessi comandi si comportano in modo diverso sull'ESP32 e quali sono i loro equivalenti MicroPython sul Raspberry Pi Pico.

Scheda Arduino Uno con i pin digitali e analogici
Arduino Uno: i pin digitali 0–13, gli ingressi analogici A0–A5 e i pin PWM segnati con ~ bastano per provare tutti i comandi di questo articolo.

Che cos'è un comando Arduino e come viene eseguito uno sketch?

Il codice Arduino si basa sul C/C++; quasi tutto quello che chiamiamo «comandi» sono in realtà funzioni che il core Arduino ti mette a disposizione già pronte. Una funzione riceve dei parametri tra parentesi (per esempio il numero di un pin e un valore) e alcune restituiscono un risultato: digitalRead() ti dà lo stato di un pin, millis() il tempo trascorso. Ogni sketch Arduino ha due funzioni obbligatorie: setup() viene eseguita una sola volta all'accensione o al reset della scheda, poi loop() si ripete all'infinito. Le preparazioni come l'impostazione dei pin e Serial.begin() vanno in setup(), mentre ciò che deve accadere di continuo va in loop().

Arduino (C++)
1// Her Arduino programı bu iki fonksiyondan oluşur / Every Arduino sketch has these two functions
2void setup() {
3  // Açılışta ya da resetten sonra bir kez çalışır / Runs once at power-up or after reset
4  pinMode(13, OUTPUT);      // dahili LED pini çıkış / built-in LED pin as output
5}
6
7void loop() {
8  // Sonsuza kadar tekrar eder / Repeats forever
9  digitalWrite(13, HIGH);   // LED'i yak / turn the LED on
10  delay(500);               // 500 ms bekle / wait 500 ms
11  digitalWrite(13, LOW);    // LED'i söndür / turn the LED off
12  delay(500);
13}

Tenere a mente tre regole fin dall'inizio ti evita molti errori di compilazione: i comandi distinguono tra maiuscole e minuscole (se scrivi digitalwrite ottieni l'errore «'digitalwrite' was not declared in this scope»), ogni istruzione termina con un punto e virgola e i testi vanno tra virgolette doppie, mentre i singoli caratteri tra apici. Per i tipi di variabili e le strutture come if e for, dai un'occhiata alle basi della programmazione Arduino.

Elenco dei comandi Arduino: tabelle per categoria

Le quattro tabelle qui sotto riassumono quasi tutti i comandi che userai nei progetti di tutti i giorni. Il primo gruppo sono i comandi di ingresso e uscita: stabiliscono il ruolo di un pin, gli applicano una tensione o ne leggono un valore. I numeri dei pin della tua scheda li trovi nello strumento Pinout.

ComandoSintassiA cosa serveEsempio
pinModepinMode(pin, modalità)Imposta un pin come ingresso o uscitapinMode(13, OUTPUT);
digitalWritedigitalWrite(pin, valore)Porta un pin a HIGH (5 V) o LOW (0 V)digitalWrite(13, HIGH);
digitalReaddigitalRead(pin)Legge se un pin è HIGH o LOWint b = digitalRead(2);
analogReadanalogRead(pin)Converte una tensione di 0–5 V in un numero da 0 a 1023int v = analogRead(A0);
analogWriteanalogWrite(pin, valore)Genera un segnale PWM da 0 a 255analogWrite(9, 128);
analogReferenceanalogReference(tipo)Sceglie la tensione di riferimento di analogReadanalogReference(INTERNAL);
tone / noTonetone(pin, frequenza, durata)Produce un suono con un'onda quadra; noTone lo fermatone(8, 440, 500);
pulseInpulseIn(pin, valore, timeout)Misura la durata di un impulso in µspulseIn(10, HIGH, 30000);
shiftOutshiftOut(pinDati, pinClock, ordine, byte)Invia un byte bit per bit (per esempio a un 74HC595)shiftOut(11, 12, MSBFIRST, 255);
attachInterruptattachInterrupt(interrupt, funzione, modalità)Chiama una funzione appena un pin cambiaattachInterrupt(digitalPinToInterrupt(2), say, FALLING);

Il secondo gruppo sono i comandi di tempo. Capire la differenza tra delay(), che aspetta, e millis() e micros(), che misurano il tempo, è la chiave per scrivere uno sketch che funzioni in modo fluido.

ComandoSintassiA cosa serveAttenzione
delaydelay(ms)Aspetta i millisecondi indicatiDurante l'attesa il programma si ferma
delayMicrosecondsdelayMicroseconds(us)Aspetta qualche microsecondoPreciso tra 3 e 16383 µs
millismillis()Restituisce i millisecondi dall'avviounsigned long; va in overflow dopo circa 49,7 giorni
microsmicros()Restituisce i microsecondi dall'avvioSull'Uno avanza a passi di 4 µs, overflow dopo circa 70 minuti

Il terzo gruppo sono i comandi di matematica: servono a convertire la lettura di un sensore in un altro intervallo, a limitarla o a generare un numero casuale. Il quarto gruppo, i comandi di comunicazione, permette alla scheda di scambiare dati con il computer e con altri dispositivi.

ComandoSintassiA cosa serveRisultato di esempio
mapmap(x, daMin, daMax, aMin, aMax)Riporta un intervallo su un altromap(512, 0, 1023, 0, 255) → 127
constrainconstrain(x, minimo, massimo)Mantiene un valore tra due limiticonstrain(300, 0, 255) → 255
min / maxmin(a, b) / max(a, b)Restituisce il minore o il maggioremin(7, 3) → 3
absabs(x)Valore assolutoabs(-12) → 12
pow / sqrt / sqpow(base, esponente), sqrt(x), sq(x)Potenza, radice quadrata, quadratopow(2, 10) → 1024.0
randomrandom(min, max)Intero casuale (max escluso)random(1, 7) → da 1 a 6
randomSeedrandomSeed(seme)Cambia il punto di partenza della sequenza casualerandomSeed(analogRead(A3));
ComandoA cosa serveEsempio
Serial.begin(velocità)Apre la porta seriale alla velocità in baud indicataSerial.begin(9600);
Serial.print(dato)Stampa il dato e resta sulla stessa rigaSerial.print(temperatura);
Serial.println(dato)Stampa il dato e va a capoSerial.println("Ciao");
Serial.available()Restituisce il numero di byte in attesa di letturaif (Serial.available() > 0)
Serial.read()Legge un byte, restituisce -1 se non ci sono datichar c = Serial.read();
Serial.readStringUntil(c)Legge il testo fino a un certo carattereString s = Serial.readStringUntil('\n');
Serial.parseInt()Estrae un intero dal testo ricevutoint n = Serial.parseInt();
Serial.write(byte)Invia un byte grezzoSerial.write(65); // 'A'
Wire.begin()Avvia il bus I2C (Wire.h)Wire.begin();
SPI.begin()Avvia il bus SPI (SPI.h)SPI.begin();

pinMode(): stabilire il ruolo di un pin

pinMode(pin, modalità) indica se un pin digitale è un ingresso o un'uscita e di solito si scrive una sola volta in setup(). Le modalità sono tre: OUTPUT rende il pin un'uscita (per pilotare un LED, un buzzer o un relè), INPUT lo rende un ingresso ad alta impedenza (per circuiti con una resistenza di pull-up o pull-down esterna) e INPUT_PULLUP lo rende un ingresso collegando a 5 V una resistenza interna di circa 20–50 kΩ. Anche i pin analogici (A0–A5) possono essere usati come pin digitali con pinMode; per esempio, pinMode(A0, OUTPUT) è un comando valido.

Se dimentichi pinMode(pin, OUTPUT) e usi digitalWrite(pin, HIGH), il pin non diventa un'uscita; sull'Uno il comando attiva soltanto la resistenza di pull-up interna. Il LED si accende, ma molto debolmente, perché la corrente deve attraversare una resistenza da 20–50 kΩ. Se il tuo LED è fioco, la prima cosa da controllare è la riga pinMode in setup().

digitalWrite() e digitalRead(): uscita e ingresso digitale

digitalWrite(pin, HIGH) porta un pin di uscita a 5 V sull'Uno e digitalWrite(pin, LOW) lo porta a 0 V. Prelevare fino a 20 mA da un pin è sicuro (il limite assoluto è 40 mA); collega sempre il LED con una resistenza, il cui valore puoi trovare con il calcolatore della resistenza per LED. digitalRead(pin) controlla la tensione sul pin e restituisce HIGH o LOW; sull'Uno, a grandi linee, tutto ciò che supera 3 V conta come HIGH e ciò che sta sotto 1,5 V come LOW. Nell'esempio seguente il LED integrato si accende quando premi il pulsante collegato a D2:

Arduino (C++)
1const int BUTON_PIN = 2;   // buton: bir ucu D2, diğer ucu GND / button: one leg to D2, the other to GND
2const int LED_PIN = 13;    // dahili LED / built-in LED
3
4void setup() {
5  pinMode(BUTON_PIN, INPUT_PULLUP);   // dahili pull-up direnci açık / internal pull-up resistor enabled
6  pinMode(LED_PIN, OUTPUT);
7}
8
9void loop() {
10  int durum = digitalRead(BUTON_PIN); // HIGH ya da LOW döner / returns HIGH or LOW
11  if (durum == LOW) {                 // pull-up ile basılı = LOW / with pull-up, pressed = LOW
12    digitalWrite(LED_PIN, HIGH);
13  } else {
14    digitalWrite(LED_PIN, LOW);
15  }
16}

Con INPUT_PULLUP la logica si inverte: con il pulsante rilasciato il pin legge HIGH grazie alla resistenza interna, quando lo premi legge LOW perché viene collegato a GND. Se colleghi il pulsante senza resistenza in modalità INPUT, il pin resta flottante e digitalRead() restituisce valori a caso; persino avvicinare la mano può cambiare il risultato. Per il rimbalzo del pulsante e i modi corretti di collegarlo, leggi come collegare un pulsante ad Arduino.

analogRead() e analogWrite(): lettura analogica e PWM

analogRead(pin) usa un convertitore a 10 bit (ADC) per trasformare una tensione tra 0 e 5 V sui pin A0–A5 in un numero da 0 a 1023. Una lettura richiede circa 100 µs, quindi puoi fare circa 10.000 letture al secondo. Per convertire il numero in tensione si usa valore × 5,0 / 1023; una lettura di 512, per esempio, corrisponde a circa 2,5 V. La tensione di riferimento si cambia con analogReference(): l'opzione INTERNAL usa il riferimento interno da 1,1 V dell'Uno e migliora la risoluzione con le tensioni piccole. Tutti i dettagli su risoluzione e riferimento sono in ADC e conversione analogico-digitale.

Nonostante il nome, analogWrite(pin, valore) non produce una vera tensione analogica. Accende e spegne il pin molto velocemente per creare un segnale PWM: 0 significa sempre spento, 255 sempre acceso e 127 acceso più o meno per metà del tempo. È da questa media che un LED ci appare a mezza luminosità e un motore gira lentamente. Sull'Uno solo i pin 3, 5, 6, 9, 10 e 11, segnati con ~ sulla scheda, forniscono il PWM; la frequenza è di 490 Hz sulla maggior parte dei pin e di 980 Hz sui pin 5 e 6. analogWrite() imposta da sola il pin come uscita, quindi pinMode non è obbligatorio. Il codice qui sotto regola la luminosità di un LED con un potenziometro e scrive la lettura insieme alla tensione nel Monitor seriale; per i dettagli su duty cycle e frequenza leggi Che cos'è il PWM?.

Arduino (C++)
1const int POT_PIN = A0;   // potansiyometrenin orta bacağı / potentiometer wiper
2const int LED_PIN = 9;    // ~9 PWM pini + 220 ohm direnç / ~9 PWM pin + 220 ohm resistor
3
4void setup() {
5  Serial.begin(9600);
6}
7
8void loop() {
9  int ham = analogRead(POT_PIN);         // 0-1023 arası / between 0 and 1023
10  float volt = ham * (5.0 / 1023.0);     // sayıyı gerilime çevir / convert the reading to volts
11  analogWrite(LED_PIN, ham / 4);         // 0-1023 -> 0-255 PWM parlaklığı / 0-1023 -> 0-255 PWM brightness
12
13  Serial.print(F("Ham / raw: "));
14  Serial.print(ham);
15  Serial.print(F("  Gerilim / voltage: "));
16  Serial.print(volt, 2);                 // 2 ondalık basamak / 2 decimal places
17  Serial.println(F(" V"));
18  delay(200);
19}
Su un pin senza PWM, analogWrite() non può produrre valori intermedi: porta il pin a LOW con valori inferiori a 128 e a HIGH da 128 in su. Anche i pin A0–A5 dell'Uno non forniscono PWM, e il classico Uno R3 non ha una vera uscita analogica (DAC); scrivere analogWrite(A0, 100) non ti darà 2 V. Se ti serve una vera tensione analogica, usa una scheda con DAC (come l'Uno R4, che ha un DAC su A0) oppure aggiungi un filtro RC a un'uscita PWM.

delay(), millis() e micros(): comandi di tempo

delay(ms) aspetta i millisecondi indicati e nel frattempo nessun'altra riga di loop() viene eseguita; delay(1000) è una pausa completa di un secondo. Per attese brevi nell'ordine dei microsecondi c'è delayMicroseconds(), preciso tra 3 e 16383 µs. millis(), invece, non aspetta: restituisce il tempo trascorso dall'avvio della scheda, in millisecondi. Salva sempre il risultato in una variabile di tipo unsigned long: se usi int, sull'Uno il valore va in overflow appena supera 32.767, cioè dopo circa 33 secondi, e la temporizzazione salta. millis() torna a zero circa ogni 49,7 giorni; il metodo della sottrazione qui sotto continua a funzionare correttamente anche con questo overflow.

Arduino (C++)
1const int LED_PIN = 13;
2const unsigned long ARALIK = 500;   // yanıp sönme aralığı (ms) / blink interval (ms)
3unsigned long oncekiZaman = 0;      // son değişim anı / time of the last toggle
4
5void setup() {
6  pinMode(LED_PIN, OUTPUT);
7}
8
9void loop() {
10  unsigned long simdi = millis();
11  if (simdi - oncekiZaman >= ARALIK) {               // taşmada da doğru çalışır / safe across overflow
12    oncekiZaman = simdi;
13    digitalWrite(LED_PIN, !digitalRead(LED_PIN));    // LED'i tersine çevir / toggle the LED
14  }
15  // Burada buton, sensör ve ekran işleri de yapılabilir / button, sensor and display tasks can run here
16}

Con questo schema loop() non si ferma mai: mentre il LED continua a lampeggiare puoi leggere un pulsante, misurare un sensore e aggiornare un display nello stesso ciclo. micros() funziona allo stesso modo in microsecondi; sull'Uno avanza a passi di 4 µs e va in overflow dopo circa 70 minuti. Spieghiamo in dettaglio come eseguire più attività insieme in Arduino millis() invece di delay().

Serial.begin(), Serial.print() e Serial.read(): comunicazione seriale

I comandi Serial permettono ad Arduino di parlare con il computer tramite il cavo USB e sono il tuo strumento di debug più importante. Serial.begin(9600) apre la porta seriale a 9600 bit al secondo; se la velocità del Monitor seriale non coincide, vedrai caratteri strani sullo schermo. Serial.print() stampa il dato e resta sulla stessa riga, mentre Serial.println() va a capo dopo la stampa. Con un secondo parametro scegli il formato: Serial.print(78, HEX) stampa «4E», Serial.print(78, BIN) stampa «1001110» e Serial.print(3.14159, 3) mostra «3.142» (i numeri decimali hanno per impostazione predefinita 2 cifre dopo la virgola). Scrivere i testi fissi dentro F("...") fa risparmiare spazio nei 2 KB di RAM dell'Uno.

Per leggere dati, prima controlli con Serial.available() se ci sono byte in attesa nel buffer; il buffer di ricezione dell'Uno è di 64 byte. Serial.read() prende un solo byte da questo buffer e restituisce -1 se non c'è niente da leggere. Il codice seguente accende e spegne il LED con i caratteri 1 e 0 che invii dal Monitor seriale:

Arduino (C++)
1const int LED_PIN = 13;
2
3void setup() {
4  Serial.begin(9600);                    // Seri Monitör de 9600 olmalı / Serial Monitor must be 9600 too
5  pinMode(LED_PIN, OUTPUT);
6  Serial.println(F("1: LED açık / on, 0: LED kapalı / off"));
7}
8
9void loop() {
10  if (Serial.available() > 0) {          // tamponda bayt var mı? / any bytes waiting?
11    char c = Serial.read();              // ilk baytı al / read the first byte
12    if (c == '1') {                      // '1' karakteri, 1 sayısı değil / the character '1', not the number 1
13      digitalWrite(LED_PIN, HIGH);
14      Serial.println(F("LED açık / on"));
15    } else if (c == '0') {
16      digitalWrite(LED_PIN, LOW);
17      Serial.println(F("LED kapalı / off"));
18    }
19    // '\n' ve '\r' gibi diğer karakterler atlanır / other characters such as '\n' and '\r' are ignored
20  }
21}

Nota che il confronto si fa con '1' (tra apici, quindi un carattere): l'1 che digiti sulla tastiera corrisponde al numero 49 nella tabella ASCII, per questo la condizione c == 1 non è mai vera. Per numeri di più cifre puoi usare Serial.parseInt(), e per leggere una riga intera Serial.readStringUntil('\n'). Per le impostazioni di fine riga del Monitor seriale, la velocità in baud e il Plotter seriale, leggi come usare il Monitor seriale di Arduino.

map() e constrain(): riscalare e limitare i valori

map(valore, daMin, daMax, aMin, aMax) sposta un numero da un intervallo a un altro in modo proporzionale. La formula che c'è dietro è: (x − daMin) × (aMax − aMin) / (daMax − daMin) + aMin. Per esempio, map(512, 0, 1023, 0, 255) restituisce 127 e non 127,6, perché map() lavora solo con numeri interi e scarta la parte decimale. Invertendo l'intervallo di destinazione ottieni una relazione inversa: map(x, 0, 1023, 255, 0). map() non limita il risultato; se l'ingresso esce dall'intervallo indicato, anche l'uscita ne esce. Per questo di solito si usa insieme a constrain(x, minimo, massimo).

Arduino (C++)
1const int LDR_PIN = A0;   // LDR + 10k gerilim bölücü / LDR + 10k voltage divider
2const int LED_PIN = 9;    // ~9 PWM pini / ~9 PWM pin
3
4void setup() {
5  pinMode(LED_PIN, OUTPUT);
6}
7
8void loop() {
9  int isik = analogRead(LDR_PIN);           // odada ölçülen aralık ~200-800 / measured range in the room ~200-800
10  int pwm = map(isik, 200, 800, 255, 0);    // karanlıkta parlak, aydınlıkta sönük / bright in the dark, dim in the light
11  pwm = constrain(pwm, 0, 255);             // aralık dışı değerleri kes / clip out-of-range values
12  analogWrite(LED_PIN, pwm);
13  delay(20);
14}

In questo esempio l'LDR dà valori tra 200 e 800 nella stanza. Se di notte la lettura scende a 100, map() calcola 297; senza constrain(), analogWrite() riceverebbe questo valore fuori dall'intervallo 0–255. Sull'Uno un valore del genere dà risultati diversi e inattesi a seconda del pin: sui pin 3, 5, 6 e 11, che usano timer a 8 bit, 297 va in overflow e si comporta come 41, quindi il LED quasi si spegne al buio completo invece di brillare al massimo. Dato che constrain(), min(), max() e abs() sull'Uno sono definite come macro, non mettere x++ né altre chiamate di funzione tra le parentesi: l'espressione può essere valutata più volte e dare risultati inattesi. Per il collegamento dell'LDR leggi sensore di luce LDR.

tone() e noTone(): produrre suoni con un buzzer

tone(pin, frequenza, durata) genera su un pin un'onda quadra con duty cycle del 50% e fa suonare a quella frequenza un buzzer passivo o un piccolo altoparlante. La durata è in millisecondi ed è facoltativa; senza di essa il suono continua finché non chiami noTone(pin). Un dettaglio importante: tone() non aspetta, avvia il suono e passa subito alla riga successiva. Per suonare note una dopo l'altra ti serve quindi, dopo ogni chiamata a tone(), un delay() lungo almeno quanto la nota, altrimenti sentirai solo l'ultima.

Arduino (C++)
1const int BUZZER_PIN = 8;   // pasif buzzer'ın + ucu / passive buzzer + leg
2
3void setup() {
4  int notalar[] = {262, 294, 330, 349, 392};   // Do Re Mi Fa Sol (Hz) / C D E F G (Hz)
5  for (int i = 0; i < 5; i++) {
6    tone(BUZZER_PIN, notalar[i], 200);         // 200 ms çal / play for 200 ms
7    delay(250);                                // tone() beklemez, sen bekle / tone() does not wait, so you do
8  }
9  noTone(BUZZER_PIN);                          // sesi kapat / stop the sound
10}
11
12void loop() {
13}
  • Limiti di frequenza: Sull'Uno non si possono generare toni sotto i 31 Hz e il limite superiore è 65.535 Hz; con un buzzer l'intervallo utile va all'incirca da 100 a 5000 Hz.
  • Un suono alla volta: Solo un pin alla volta può emettere un tono; per suonare su un altro pin devi prima fermare quello precedente con noTone().
  • Conflitto con il PWM: Sull'Uno tone() usa il Timer2, quindi mentre suona analogWrite() sui pin 3 e 11 non funziona correttamente.
  • Buzzer attivo: Avendo un oscillatore proprio, la sua frequenza non si può cambiare con tone(); basta accenderlo e spegnerlo con digitalWrite(). Le differenze sono spiegate in Che cos'è un buzzer?.

pulseIn(): misurare la durata di un impulso

pulseIn(pin, HIGH) aspetta che il pin diventi HIGH, misura per quanto tempo resta HIGH e restituisce il risultato in microsecondi; può misurare impulsi da 10 µs a 3 minuti. L'uso più noto è il sensore di distanza a ultrasuoni HC-SR04: il sensore fornisce un impulso lungo quanto il tempo che il suono impiega ad andare e tornare. Il suono viaggia nell'aria a circa 343 m/s, cioè 0,0343 cm/µs; dato che il percorso è di andata e ritorno, distanza = durata × 0,0343 / 2.

Arduino (C++)
1const int TRIG_PIN = 9;
2const int ECHO_PIN = 10;
3
4void setup() {
5  Serial.begin(9600);
6  pinMode(TRIG_PIN, OUTPUT);
7  pinMode(ECHO_PIN, INPUT);
8}
9
10void loop() {
11  digitalWrite(TRIG_PIN, LOW);
12  delayMicroseconds(2);
13  digitalWrite(TRIG_PIN, HIGH);            // 10 µs tetik darbesi / 10 µs trigger pulse
14  delayMicroseconds(10);
15  digitalWrite(TRIG_PIN, LOW);
16
17  unsigned long sure = pulseIn(ECHO_PIN, HIGH, 30000UL);   // en fazla 30 ms bekle / wait at most 30 ms
18  if (sure == 0) {
19    Serial.println(F("Menzil dışı / out of range"));
20  } else {
21    float mesafe = sure * 0.0343 / 2.0;    // ses hızı 343 m/s, gidiş-dönüş / speed of sound 343 m/s, round trip
22    Serial.print(mesafe, 1);
23    Serial.println(F(" cm"));
24  }
25  delay(100);
26}

Il terzo parametro, il timeout, è molto importante. Se manca, pulseIn() aspetta 1 secondo quando non arriva nessun impulso e restituisce 0; se il cavo del sensore si stacca, l'intero programma si blocca per un secondo a ogni ciclo. Un timeout di 30000 µs corrisponde a circa 5 metri, sufficienti per i 4 metri di portata dell'HC-SR04. Per il collegamento e la precisione del sensore leggi sensore di distanza HC-SR04.

attachInterrupt(): reagire all'istante con gli interrupt

attachInterrupt(digitalPinToInterrupt(pin), funzione, modalità) esegue la funzione indicata nell'istante in cui un pin cambia, qualunque cosa stia facendo loop() in quel momento. Questa funzione si chiama routine di servizio dell'interrupt (ISR). La modalità può assumere quattro valori: RISING scatta nel passaggio da LOW a HIGH, FALLING nel passaggio da HIGH a LOW, CHANGE a ogni cambiamento e LOW finché il pin resta LOW. Su Uno e Nano solo i pin D2 e D3 possono generare interrupt in questo modo; sul Mega si possono usare i pin 2, 3, 18, 19, 20 e 21. È l'ideale per non perdere un encoder che gira veloce, gli impulsi di un anemometro o una pressione molto breve del pulsante.

Arduino (C++)
1const int BUTON_PIN = 2;            // Uno'da kesme pinleri: 2 ve 3 / interrupt pins on the Uno: 2 and 3
2volatile unsigned long sayac = 0;   // ISR'de değişen değişken volatile olmalı / variables changed in an ISR must be volatile
3
4void say() {                        // kesme fonksiyonu (ISR): kısa tut / interrupt service routine: keep it short
5  sayac++;
6}
7
8void setup() {
9  Serial.begin(9600);
10  pinMode(BUTON_PIN, INPUT_PULLUP);
11  attachInterrupt(digitalPinToInterrupt(BUTON_PIN), say, FALLING);   // HIGH'dan LOW'a geçişte / on a HIGH-to-LOW edge
12}
13
14void loop() {
15  noInterrupts();                   // 4 baytlık değeri güvenle kopyala / copy the 4-byte value safely
16  unsigned long kopya = sayac;
17  interrupts();
18
19  Serial.print(F("Basma sayısı / presses: "));
20  Serial.println(kopya);
21  delay(500);
22}
Dichiara come volatile ogni variabile che modifichi in una ISR e tieni la funzione più corta possibile. Dentro una ISR delay() non funziona, millis() non avanza e Serial.print() può bloccare il programma; aggiorna solo un flag o un contatore e fai il lavoro vero in loop(). I pulsanti meccanici rimbalzano, quindi con una sola pressione il contatore può aumentare più volte; la soluzione al rimbalzo e tutti i dettagli sugli interrupt sono in interrupt con Arduino.

random() e randomSeed(): generare numeri casuali

random(max) restituisce un intero casuale tra 0 e max−1, e random(min, max) uno tra min e max−1; il limite superiore non è compreso nel risultato. Per questo, per un dado da 1 a 6 si scrive random(1, 7). Arduino non produce una vera casualità: calcola una sequenza di numeri con una formula fissa, quindi se non chiami randomSeed() vedrai gli stessi numeri nello stesso ordine a ogni avvio della scheda. Usare come seme il rumore di un pin analogico non collegato risolve il problema nella pratica:

Arduino (C++)
1void setup() {
2  Serial.begin(9600);
3  randomSeed(analogRead(A3));     // boştaki A3 pininin gürültüsü / noise from the unconnected A3 pin
4}
5
6void loop() {
7  int zar = random(1, 7);         // 1-6 arası; üst sınır dahil değil / 1 to 6; the upper bound is exclusive
8  Serial.print(F("Zar / dice: "));
9  Serial.println(zar);
10  delay(1000);
11}

Comandi Arduino sull'ESP32: a cosa fare attenzione

L'ESP32 si programma nell'IDE Arduino con gli stessi comandi, e la maggior parte del codice di questo articolo funziona subito cambiando i numeri dei pin. Ma dato che l'hardware è diverso, alcuni comandi si comportano in un altro modo. La differenza più importante è la tensione: i pin dell'ESP32 lavorano a 3,3 V, digitalWrite(pin, HIGH) fornisce 3,3 V e non bisogna collegare segnali a 5 V direttamente ai pin. Per conoscere meglio la scheda leggi Che cos'è l'ESP32?.

ComandoArduino UnoESP32
digitalWrite(pin, HIGH)5 V3,3 V
analogRead()10 bit, 0–1023, A0–A512 bit, 0–4095; con il Wi-Fi attivo solo i pin ADC1 (GPIO 32–39)
analogWrite()Solo i pin ~ (3, 5, 6, 9, 10, 11), 490/980 HzQuasi ogni GPIO in grado di fare da uscita; frequenza con ledcAttach(pin, frequenza, risoluzione)
attachInterrupt()Solo D2 e D3Tutti i GPIO; alla ISR si aggiunge IRAM_ATTR
tone()Usa il Timer2 e disturba il PWM su 3 e 11Funziona con l'hardware LEDC
Serial.begin()Di solito 9600Di solito 115200 (anche i messaggi di avvio usano questa velocità)
delay()Tiene il processore completamente in attesaCede il tempo a FreeRTOS; il Wi-Fi continua a funzionare in background
Arduino (C++)
1const int POT_PIN = 34;     // ADC1 pini, Wi-Fi açıkken de okur / ADC1 pin, works with Wi-Fi on
2const int LED_PIN = 2;      // çoğu DevKit'te dahili LED / onboard LED on most DevKits
3const int BUTON_PIN = 4;    // butonun diğer ucu GND / other button leg to GND
4
5volatile bool basildi = false;
6
7void IRAM_ATTR butonISR() { // ESP32'de ISR IRAM'e yerleşmeli / on the ESP32 the ISR should live in IRAM
8  basildi = true;
9}
10
11void setup() {
12  Serial.begin(115200);
13  pinMode(BUTON_PIN, INPUT_PULLUP);
14  attachInterrupt(digitalPinToInterrupt(BUTON_PIN), butonISR, FALLING);   // her GPIO kesme destekler / every GPIO supports interrupts
15}
16
17void loop() {
18  int ham = analogRead(POT_PIN);                     // 0-4095 (12 bit)
19  analogWrite(LED_PIN, map(ham, 0, 4095, 0, 255));   // 8 bit PWM / 8-bit PWM
20  if (basildi) {
21    basildi = false;
22    Serial.println("Butona basıldı / button pressed");
23  }
24  Serial.println(ham);
25  delay(200);
26}

Poiché sull'ESP32 analogRead() restituisce valori da 0 a 4095, anche l'intervallo di map() cambia di conseguenza. IRAM_ATTR davanti alla ISR colloca il codice nella memoria interna veloce; se manca, in alcune situazioni la scheda può riavviarsi con un «Guru Meditation Error». Le letture analogiche dell'ESP32 non sono lineari vicino agli estremi (intorno a 0 V e a 3,3 V); se ti serve una misura più precisa, la funzione analogReadMilliVolts() restituisce un valore calibrato in millivolt.

Raspberry Pi Pico: gli equivalenti MicroPython dei comandi Arduino

Se programmi il Raspberry Pi Pico con l'IDE Arduino (core arduino-pico), tutti i comandi di questo articolo funzionano con lo stesso nome; analogRead() restituisce 10 bit per impostazione predefinita e analogWrite() genera un PWM a 1 kHz. Il Pico però si usa soprattutto con MicroPython. Lì i comandi si scrivono con oggetti dei moduli machine e time; la logica è la stessa, cambia solo la sintassi. Anche i pin del Pico lavorano a 3,3 V e gli ingressi analogici sono i pin GP26, GP27 e GP28. Per cominciare leggi Che cos'è il Raspberry Pi Pico?.

Comando ArduinoEquivalente MicroPythonNota
pinMode(15, OUTPUT)led = Pin(15, Pin.OUT)L'oggetto Pin si crea una volta sola
pinMode(14, INPUT_PULLUP)btn = Pin(14, Pin.IN, Pin.PULL_UP)Resistenza di pull-up interna
digitalWrite(15, HIGH)led.value(1)Va bene anche led.on()
digitalRead(14)btn.value()Restituisce 0 o 1
analogRead(A0)ADC(26).read_u16()Restituisce da 0 a 65535
analogWrite(15, 128)PWM(Pin(15)).duty_u16(32768)Duty cycle da 0 a 65535
delay(500)time.sleep_ms(500)Va bene anche time.sleep(0.5)
millis()time.ticks_ms()Per le differenze, time.ticks_diff()
Serial.println(x)print(x)Compare nella shell di Thonny
tone(pin, 440)pwm.freq(440) e pwm.duty_u16(32768)Per zittirlo, duty_u16(0)
pulseIn(pin, HIGH, 30000)machine.time_pulse_us(pin, 1, 30000)In caso di timeout restituisce un valore negativo
attachInterrupt(...)btn.irq(trigger=Pin.IRQ_FALLING, handler=f)f riceve l'oggetto pin come parametro
random(1, 7)random.randint(1, 6)Il limite superiore è compreso
map() / constrain()Non sono incluse, le scrivi tuPer constrain: min(max(x, 0), 255)
MicroPython
1from machine import Pin, ADC, PWM
2import time
3
4led = PWM(Pin(15))                       # pinMode + analogWrite karşılığı / pinMode + analogWrite equivalent
5led.freq(1000)                           # 1 kHz PWM
6buton = Pin(14, Pin.IN, Pin.PULL_UP)     # INPUT_PULLUP karşılığı / INPUT_PULLUP equivalent
7pot = ADC(26)                            # GP26 = ADC0
8
9def map_deger(x, giris_min, giris_max, cikis_min, cikis_max):   # Arduino map() karşılığı / Arduino map() equivalent
10    return (x - giris_min) * (cikis_max - cikis_min) // (giris_max - giris_min) + cikis_min
11
12onceki = time.ticks_ms()                 # millis() karşılığı / millis() equivalent
13while True:
14    ham = pot.read_u16()                 # 0-65535 (analogRead)
15    led.duty_u16(ham)                    # parlaklık / brightness (analogWrite)
16    if buton.value() == 0:               # digitalRead; basılı = 0 / pressed = 0
17        print("Butona basıldı / button pressed")
18    if time.ticks_diff(time.ticks_ms(), onceki) >= 1000:   # saniyede bir / once a second
19        onceki = time.ticks_ms()
20        print("Yüzde / percent:", map_deger(ham, 0, 65535, 0, 100))
21    time.sleep_ms(50)                    # delay(50)

Come vedi nella tabella, in MicroPython random.randint(1, 6) comprende il limite superiore e dà lo stesso risultato di random(1, 7) su Arduino; time.ticks_diff() garantisce la stessa sicurezza contro l'overflow della sottrazione con millis(). Sono proprio queste piccole differenze a creare più confusione quando si passa da una piattaforma all'altra. Per gli altri comandi di base di MicroPython dai un'occhiata alle basi di MicroPython.

Idee di progetto con questi comandi

  • Semaforo: Accendi tre LED in sequenza con pinMode(), digitalWrite() e delay(); lo spieghiamo passo passo in semaforo con Arduino.
  • Dado elettronico: Genera un numero da 1 a 6 con random() e, quando premi il pulsante, mostra il risultato con i LED o su un display a 7 segmenti.
  • Sensore di parcheggio: Misura la distanza con pulseIn(), trasformala in un intervallo di bip con map() ed emetti un avviso sonoro con tone().
  • Luce notturna automatica: Leggi la luce con analogRead() e regola la luminosità di analogWrite() con map() e constrain().
  • Telecomando via porta seriale: Controlla un LED, un relè o un servomotore con i comandi inviati dal computer tramite Serial.read().
  • Test dei riflessi: Accendi un LED in un momento casuale con random() e misura il tuo tempo di reazione al millisecondo con attachInterrupt() e millis().

Errori comuni e come risolverli

ErroreSintomoSoluzione
pinMode() dimenticatoIl LED è molto fioco o non si accendeScrivi pinMode(pin, OUTPUT) in setup()
Pulsante senza resistenza in modalità INPUTdigitalRead() restituisce 0 e 1 a casoUsa INPUT_PULLUP; premuto si legge LOW
Errore di battitura nel nome del comando'digitalwrite' was not declared in this scopeAttenzione a maiuscole e minuscole: digitalWrite
Velocità in baud diversaCaratteri strani nel Monitor serialeImposta il Monitor seriale alla stessa velocità di Serial.begin()
analogWrite() su un pin senza PWMIl LED è solo acceso al massimo o spentoUsa un pin segnato con ~ (3, 5, 6, 9, 10, 11)
millis() salvato in un intLa temporizzazione salta dopo circa 33 secondiUsa unsigned long
Dado con random(1, 6)Il 6 non esce maiScrivi random(1, 7); il limite superiore è escluso
Aspettarsi decimali da map()Il risultato è sempre intero e le piccole variazioni si perdonoSe serve, scrivi una tua formula con float
pulseIn() senza timeoutSenza sensore ogni ciclo si blocca per 1 secondoPassa 30000 come terzo parametro
delay() o Serial dentro una ISRIl programma si blocca o si comporta in modo stranoNella ISR cambia solo un flag volatile
Se non sei sicuro di cosa faccia esattamente un comando, scrivi un piccolo sketch di prova e stampa il risultato con Serial.println(); nell'IDE Arduino 2 puoi anche fare clic destro sul nome del comando e scegliere «Vai alla definizione» per vederne il codice sorgente. Se il codice non si compila, la prima riga del messaggio di errore di solito indica dov'è il problema; i messaggi più comuni e le loro soluzioni sono in errori di compilazione Arduino.

I comandi di questo articolo sono i mattoni della maggior parte dei progetti che realizzerai con Arduino; il resto sono per lo più funzioni aggiuntive fornite dalle librerie. Provare ogni comando una volta nel suo piccolo circuito e vedere il risultato nel Monitor seriale ti fa imparare molto meglio che studiarli a memoria. Nell'app BlueGrays trovi, passo passo e con schemi elettrici e codice commentato, progetti che usano questi comandi, come accendere un LED, controllare un LED con un pulsante, misurare la luce con un LDR, regolare la frequenza di un buzzer, misurare la distanza con l'HC-SR04 e un progetto del dado.

Domande frequenti

Quali sono i comandi Arduino più usati?

I più frequenti sono pinMode(), digitalWrite(), digitalRead(), analogRead(), analogWrite(), delay(), millis(), Serial.begin() e Serial.println(). Se aggiungi map() per riscalare i valori, tone() per i suoni, pulseIn() per i sensori di distanza e attachInterrupt() per gli interrupt, puoi scrivere quasi tutti i tuoi progetti da principiante.

In che linguaggio si scrivono i comandi Arduino?

Il codice Arduino si basa su C e C++; il core Arduino aggiunge al linguaggio funzioni già pronte come pinMode() e digitalWrite(). I comandi sono in inglese, distinguono tra maiuscole e minuscole e ogni istruzione termina con un punto e virgola. ESP32 e Raspberry Pi Pico, se preferisci, si possono programmare anche in MicroPython.

Che differenza c'è tra digitalWrite e analogWrite?

digitalWrite() porta un pin in uno di due soli stati: HIGH (5 V sull'Uno) o LOW (0 V). analogWrite() accende e spegne il pin molto velocemente per creare un segnale PWM da 0 a 255, con cui puoi regolare la luminosità di un LED o la velocità di un motore. Sull'Uno analogWrite() funziona solo sui pin segnati con ~.

Cosa significano void setup e void loop?

setup() è la funzione che viene eseguita una volta all'accensione o al reset della scheda; qui vanno le preparazioni come pinMode() e Serial.begin(). loop() è la funzione che si ripete all'infinito dopo la fine di setup(). Il void davanti indica che queste funzioni non restituiscono alcun valore.

Che differenza c'è tra Serial.print e Serial.println?

Serial.print() stampa il dato e lascia il cursore sulla stessa riga, così il dato successivo compare subito accanto. Serial.println() dopo la stampa invia i caratteri di fine riga (\r\n), quindi il dato successivo inizia su una nuova riga. Per mostrare più valori affiancati su una riga si usa prima print() e alla fine println().

A cosa serve la funzione map di Arduino?

map() converte un numero da un intervallo a un altro in modo proporzionale; per esempio trasforma il valore 0–1023 di analogRead() nell'intervallo 0–255 per analogWrite() o in 0–180 gradi per un servo. Lavora solo con interi e non limita il risultato, quindi è consigliabile usarla insieme a constrain() per gestire gli ingressi fuori intervallo.

Vedi tutti i progetti
LED lampeggiante
#1Principiante

LED lampeggiante

Progetto di input di base che accende e spegne un singolo LED a determinati intervalli con Arduino. Insegna l'uso delle funzioni digitalWrite e delay.

4.6(171)
903
Accendere un LED con un pulsante (Push)
#9Principiante

Accendere un LED con un pulsante (Push)

Progetto di controllo istantaneo (push) che accende il LED quando il pulsante viene premuto e lo spegne quando viene rilasciato. Insegna l'uso della lettura dei pulsanti e dell'espressione condizional

4.9(112)
190
Misurare l'intensità luminosa con LDR
#11Principiante

Misurare l'intensità luminosa con LDR

Progetto che misura l'intensità della luce ambientale con un sensore LDR e la visualizza su un monitor seriale. Insegna le basi della lettura dei sensori analogici e della comunicazione seriale.

4.6(78)
124
Impostazione della frequenza con cicalino
#14Intermedio

Impostazione della frequenza con cicalino

Progetto interattivo con 4 pulsanti che possono aumentare e diminuire la frequenza del cicalino, accenderlo e spegnerlo. Insegna la lettura multipla dei pulsanti e il controllo variabile.

4.6(68)
112
HC-SR04 Misura della distanza (display seriale)
#27Principiante

HC-SR04 Misura della distanza (display seriale)

Il sensore a ultrasuoni HC-SR04 misura la distanza e visualizza il risultato in cm sul monitor seriale. Insegna il principio del calcolo della distanza con l'onda sonora.

4.6(44)
66
Effetto Supercar a velocità regolabile
#8Intermedio

Effetto Supercar a velocità regolabile

Progetto con potenziometro in grado di regolare in tempo reale la velocità dell'animazione LED Supercar. Combina ingresso analogico e controllo bidirezionale ad anello.

4.5(59)
115
Funzionamento del servomotore con potenziometro
#36Principiante
Premium

Funzionamento del servomotore con potenziometro

Progetto che controlla il servomotore in tempo reale convertendo il valore del potenziometro nell'intervallo 0-180°. insegna la funzione map() e l'integrazione analogico-servo.

4.6(37)
51
Progetto Dadi
#79Intermedio
Premium

Progetto Dadi

Progetto di dado che genera un numero casuale tra 1 e 6 a ogni pressione del pulsante e lo visualizza su un display a 7 segmenti. Grazie al meccanismo di rimbalzo, viene prodotto un unico risultato co

5.0(48)
67