Semplice lanciarazzi Lego

Inizierei con il dare nomi espliciti ai pin tramite apposite costanti, perché pur guardando ho capito solo che il 3 è il punto (in questo senso il codice non è autodocumentante, immagina di rileggerlo tra un anno senza il circuito sotto mano).

Diamo per scontato che i segmenti hanno questa disposizione?

//      A
//     ---
//  F |   | B
//     -G-
//  E |   | C
//     --- .DP
//      D

Compiliamo allora una serie di costanti iniziali per usare i nomi dei segmenti nel programma al posto dei numeri dei pin:

const uint8_t A_PIN  = 
const uint8_t B_PIN  = 
const uint8_t C_PIN  = 
const uint8_t D_PIN  = 
const uint8_t E_PIN  = 
const uint8_t F_PIN  = 
const uint8_t G_PIN  = 
const uint8_t DP_PIN = 3;

Poi possiamo creare un array di pin utile per scorrere i segmenti con un indice da 0 a 7.
Nota: in questo modo non c'è neppure più bisogno che i pin a cui sono collegati i segmenti siano in sequenza numerica:

const uint8_t SEG_PIN_ARRAY[] = { 
    A_PIN, B_PIN, C_PIN, D_PIN,
    E_PIN, F_PIN, G_PIN, DP_PIN
};

A questo punto le seguenti istruzioni sono equivalenti:

digitalWrite(3, HIGH);
digitalWrite(DP_PIN, HIGH);
digitalWrite(SEG_PIN_ARRAY[7], HIGH);

La prima non la usiamo più, perché lo scopo è non vedere più numeri di pin in giro per il programma (si parametrizza tutto con nomi costanti all'inizio).
La seconda è utile per agire sul singolo segmento.
La terza per scorrere i segmenti in automatico con un ciclo, ad esempio per spegnere tutto:

for (uint8_t i=0; i<=7; i++)
    digitalWrite(SEG_PIN_ARRAY[i], LOW);

Non serve ogni volta specificare il pinMode. Se non ci sono motivi tecnici per volerlo fare, basta una sola volta nel setup e i pin mantengono la "direzione" per tutto il tempo.


Nota: uint8_t indica semplicemente un byte, un intero a 8 bit senza segno (U)nsigned.