Quando qualcosa diventa pesante da gestire... è il momento di scomporre, vuol dire che in quel punto di codice si vogliono gestire troppi dettagli di processi che potrebbero essere indipendenti, benché coordinati tra loro.
Invece di gestire ogni uscita nelle fasi... ogni uscita può essere un processo indipendente, con una propria fase interna, che legge la fase principale attiva, e si comporta di conseguenza.
E con questo è terminata la scomposizione dei compiti.
Il passo successivo se si vuole è strutturare il tutto in modo diverso.
Una prima cosa che si può dire è che quegli array di dati potrebbero essere trasformati in array di struct, dove ogni elemento (una struct) contiene tutte le variabili necessarie per processare il comportamento di un'uscita. Certo posso passare un indice a una funzione, e con quell'indice la funzione trova tutti i dati nei diversi array. Oppure posso passare una struct con tutti i dati, e la funzione lavora in modo uguale sull'insieme di dati di volta in volta ricevuti.
Se vogliamo è quello che viene chiamato "zucchero sintattico" (come switch vs if else), non cambia la logica, ma può rendere il tutto più semplice da gestire mentalmente, e soprattutto è propedeutico al successivo livello di strutturazione dei programmi, cioè gli oggetti.
Anche gli oggetti usati in modo base sono zucchero sintattico, in fondo non si fanno magie e la CPU sempre le solite quattro cose sa fare (memorizzare, calcolare, comunicare, saltare), ma cambiano il modo di pensare e progettare un programma.
Ora il caso in questione è un tipico esempio in cui una sola classe può descrivere perfettamente dati e comportamento di ciascuna di quelle uscite. Per ciascuna uscita dalla classe si "stampa" l'oggetto corrispondente, et voilà, un array di oggetti, ciascuno con le sue variabili "incapsulate" e indipendenti da quelle degli altri, e con la logica elaborativa annessa richiamabile con la notazione punto oggetto.metodo()
Aggiungere un'altra uscita diventa banale, si "stampa" dalla classe un oggetto in più passandogli i dati di configurazione.
Come esempio a cui ispirarsi, forse non stilisticamente corretto (in C++ con le classi e i namespace sono una capra):
class Punto
{
public:
float x;
float y;
// costruttore inizializza i dati
Punto(float xx, float yy)
{
x = xx;
y = yy;
}
// metodo per chiedere a un punto quanto
// e` distante da un altro punto
float distanza(Punto p)
{
float dx = x - p.x;
float dy = y - p.y;
return sqrt(dx*dx + dy*dy);
}
};
// crea (istanzia) due oggetti tipo Punto
// con specifiche coordinate
Punto a(30.5, 127.8);
Punto b(-387.6, 860.1);
void setup()
{
Serial.begin(9600);
// sia a che b dichiarano di trovarsi
// alla stessa distanza dall'altro punto
Serial.println(a.distanza(b));
Serial.println(b.distanza(a));
}
void loop() {}