Dichiarazione condizionale di variabili

Ciao a tutti

Ho inventato un prova cavi LAN (UTP) con un ATmega328p che trasmette impulsi diversi e un altro che li identifica e visualizza sul display lo stato del cavo. I due microcontrollori devono fare cose diverse, perciò sembra ovvio che debbano essere programmati diversamente.

Fino a qualche giorno fa era così; poi, però, ho pensato di mettere insieme i due programmi, con un pin che seleziona quale parte del programma deve essere usata: letto lo stato del pin all'accensione, l'esecuzione entra definitivamente in un while(1) o nell'altro, eseguendo il programma richiesto.

Per fare ciò, fin dal setup l'esecuzione ha due strade diverse, che vengono selezionate prima nel setup e poi nel loop.

Per quanto riguarda le variabili, invece, non ho trovato altro modo che dichiararle tutte insieme all'inizio.
Tutto funziona benissimo e non ci sono assolutamente problemi di memoria, ma mi è venuta la curiosità: se dovessi realizzare qualcosa di simile con molte variabili, potrei dichiarare solo quelle che servono per la parte di programma che è stata selezionata? Potrei farle locali in ciascuno dei due while(1)?

Grazie
Gianluca

Secondo me, a dichiararle locali, ti infogni nel doverle passare, poi, ai vari metodi.

Il fatto di usare un pin per pilotare l'esecuzione, ok.
Il fatto di bypassare il loop e ricrearlo ad valorem pin nel setup non mi piace particolarmente.
Io avrei fatto una if nel loop e poi richiamato due funzioni dove mettere le due logiche.

Questo ti avrebbe permesso di switchare modalità anche in corso d'opera.

Se invece il cambio modalità non è previsto, mi chiedo perchè non tenere separati i due sketch fin dall'inizio.

Se lo scopo è di avere un unico dispositivo e di saldare il pin all'inizio battezzando l'uso che ne vorrai fare, ok, mi adeguo al requisito e ti propongo un accrocchio.

Riutilizzo delle variabili, per quanto possibile.
Cioè ti mappi le variabili dello stesso tipo che devi dichiarare globali e le usi in modo diverso nei due loop.
Per non incasinarti con i nomi, li rimappi nei due loop con delle define.

Nella dichiarazione, a questo punto, puoi dare nomi casuali, poi i nomi che ti serviranno per comprendere il programma le stabilisci nelle define.

Ad es.:

// Variabili globali
int var1 = 0;

#define INDICE var1
#define POSIZIONE var1

void loop() {
  if (letturaPinStatico()) { // Può pure essere letto nella setup e qui testata solo la variabile...
    loop1();
  } else {
    loop2();
  }

voi loop1() {
  INDICE++;
}

void loop2() {
  POSIZIONE--;
}

Risparmi lo spazio di tutte le variabili che riesci a riutilizzare.
Ovviamente si parla di variabili globali, perchè per quelle locali il problema non si pone, come dicevi anche tu.

Maurizio

In effetti, avrei anche potuto lasciare vuoto il loop, mettendo i due while nel setup. Non avendolo pensato fin da principio, però, mi è venuto spontaneo metterli nel loop che, in realtà, non gira più fino alla fine: il ciclo rimane confinato all'interno di uno dei due while nel loop.

Per quanto riguarda le variabili, ci sono variabili uguali come quella della lettura della tensione della batteria, la cui dichiarazione serve in entrambi i casi, ma gli array dei simboli per lo stato della batteria, ad esempio, non servono nel trasmettitore, che non ha display. L'uso dei #define, però, è interessante.

Usare degli if complicherebbe la struttura del programma mischiando le sezioni, rendendone più difficile la comprensibilità e rallentando anche l'esecuzione che, dovendo essere prodotti segnali di durata precisa e poi identificati in base alla durata, è un po' critica.

Ripeto: tutto funziona e questa è solo una curiosità che mi è venuta per imparare.

I vantaggi del microcontrollore singolo sono:

  • Volendo conservare delle scorte in caso di guasto, basta averne uno solo.
  • In caso di problemi, è possibile scambiarli per fare prove.
  • Bisogna conservare un solo file.
  • Apportando una modifica al programma del Tx o dell'Rx, si salva il file con la versione aggiornata e basta, senza dover zippare insieme ad esempio Tx_v3.1 e Rx_v3.5: sarà TxRx_v3.5 e basta.

La tua mi sembra la soluzione più pratica compatibile con il codice attuale, stravolgere il codice per risparmiare memoria ram è da valutare solo nel caso la ram fosse insufficiente.

Comunque ci sono almeno due modi:
Allocare ram dinamicamente e l'allocamente locale.

Con il primo c'è maggiore libertà e non ci sono controindicazioni simili a String a patto che l'allocazione avvenga una sola volta.

Con il secondo la void loop() rompe confrontata con la main() del C++.

struct GVarA {
    uint8_t type;
    char *name;
};

struct GVarB {
    uint8_t type;
    char *name;
};

void loop1(struct GVarA *gVarPtr) {
    while(1) {

    }
}

void loop2(struct GVarB *gVarPtr)
{
    while(1) {

    }
}
int main()
{
    if (1) {
        struct GVarA gVarA;
        loop1(&gVarA);

    } else {
        struct GVarB gVarB;
        loop2(&gVarB);
    }
}

Con la void loop() di arduino non si può fare. Ma anche così c'è da vedere il disassemblato
per capire cosa viene allocato e cosa no.

Mentre con l'allocazione dinamica puoi allocare nella setup() con new, tipo:

void setup() {
    if (1) {
        struct GVarA *gVarA = new struct GVarA;
   } else {
        struct GVarB *gVarB = new struct GVarB; 
   } 
}

Ho semplificato estremamente il codice e l'ultimo non è testato, spero si capisca qualcosa.

Ciao.

Sicuramente si capisce, ma struct per me è ancora ignoto. Devo studiarlo.

ma struct per me è ancora ignoto. Devo studiarlo.

Studia struct C++ poiché struct in C++ è uguale a class con la differenza che in struct tutto è pubblico mentre in class ci possono essere variabili (sarebbe coretto chiamarli "membri"): public, private e protected.

Sia per class che per struct, per accedere ad un membro si usa l'operatore '.' (punto).
Se vi accedi attraverso un puntatore si usa '->' al posto del punto.

C'è un altro modo per cambiare loop al primo avvio:

// typedef void (*voidFuncPtr)(void); come è definito il tipo puntatore in wiring_private.h

voidFuncPtr ptrFunc = 0;  // ptrFunc è il puntatore a funzione

void setup() {

     if (1) {
         ptrFunc = function1;
     } else {
        ptrFunc = function2;
         
     }

}


void loop() {
     ptrFunc();  // chiamata a funzione 
}

void function1() {

}

void function2() {

}

Così il loop inizia e termina come di consueto a meno che in function1 e 2 ci mette il while(1).

Ciao.

Sempre più misterioso... :slight_smile:
if(1) non è sempre vera, escludendo il successivo else?...

if(1) non è sempre vera, escludendo il successivo else?...

Daiii, non dire così, che ci devo mettere al posto di 1, digitalRead(), come faccio a sapere il codice che usi tu per decidere quale anima del software attivare.
Ovvio che entra sempre nella prima if. 8)

Ciao.

Perdonami: credevo che ci andasse qualcosa di ignoto :slight_smile:

Uhmm...

// typedef void (*voidFuncPtr)(void); come è definito il tipo puntatore in wiring_private.h
voidFuncPtr ptrFunc = 0;  // ptrFunc è il puntatore a funzione

#define PIN_MODO A3
byte bat; // Stato della batteria (0, 1, 3, 5, 7).

void setup() 
{
if (digitalRead(PIN_MODO))
  {
  ptrFunc = trasmettitore;
  }
else 
  {
  ptrFunc = ricevitore;
  lcd.begin(16, 2);
  // Il display può caricare in memoria un massimo di 8 simboli.
  lcd.createChar(0, Batt_0); //  <6.6V
  lcd.createChar(1, Batt_1); // >=7V 
  lcd.createChar(3, Batt_3); // >=7V5 
  lcd.createChar(5, Batt_5); // >=8V 
  lcd.createChar(7, Batt_7); // >=8V5
  // ...
  }

void loop() 
{ptrFunc();}  // chiamata a funzione

void trasmettitore() 
{
}

void ricevitore() 
{
byte Batt_0[8]={14,17,19,17,21,17,25,31};
byte Batt_1[8]={14,17,17,17,17,17,17,31};
byte Batt_3[8]={14,17,17,17,17,31,31,31};
byte Batt_5[8]={14,17,17,31,31,31,31,31};
byte Batt_7[8]={14,31,31,31,31,31,31,31};
// ...
}

Non può funzionare... Per gli lcd.createChar devo aver già dichiarato le variabili Batt_x!

Beh... Credo che, se mi servirà di nuovo, farò esattamente come ho già fatto :slight_smile:

Beh... Credo che, se mi servirà di nuovo, farò esattamente come ho già fatto

Non ci capiamo, quello del puntatore a funzione non ti risolve l'allocamento e solo una delle tante alternative possibili per scegliere quale funzione eseguire.

Tu vuoi fare tutto assieme.

voidFuncPtr ptrFunc = 0;  // ptrFunc è il puntatore a funzione

#define PIN_MODO A3


void setup()
{
    Serial.begin(57600);

    pinMode(PIN_MODO, INPUT_PULLUP);
    if (digitalRead(PIN_MODO)) {
        setupTrasmettitore();
        ptrFunc = trasmettitore;
    } else {
        setupRicevitore();
        ptrFunc = ricevitore;
    }
}

void loop() {

    ptrFunc(); // chiamata a funzione

}

void setupTrasmettitore() {
    Serial.println("setupTrasmettitore");

}

void trasmettitore() {
    Serial.println("Trasmettitore");
    delay(1000);
}

void setupRicevitore() {
    Serial.println("setupRicevitore");
}

void ricevitore() {
    Serial.println("ricevitore");
    delay(1000);
}

Ciao.

Grazie
Ciao