Sheet Arduino n.8 (Arduino serial communication)

[Lezione del 03 e 09 dic 2019]

Comunicazione seriale tra Arduino

La scheda della esercitazione n.8, richiede di effettuare comunicazione tra due microcontrollori Arduino.

La scheda procede per step in tre diverse fasi. Io ho approcciato direttamente l’ultima fase, che consiste nell’inviare ad un Arduino slave un comando che indica come accendere/spegnere due LED controllati dallo slave.

Il comando è inviato da un arduino master che controlla due pulsanti. Premendo uno dei pulsanti si comanda l’accensione del corrispondente LED tramite lo slave.

Avendo a disposizione un Elegoo 2560 (clone dell’arduino 2560), ho usato questo come master. Questo perchè il 2560, a differenza dell’arduino UNO, è dotato di più seriali. In tal modo ho potuto controllare per debugging come avviene la comunicazione tra master e slave facendo l’echo da slave del comando ricevuto, e inviando quest’ultimo alla seriale 0 del 2560 che è collegata verso il computer.

Il seguente video sintetizza l’esercizio:

Qui di sketch ne abbiamo due: uno per lo slave e uno per il master.

Iniziamo con il più semplice. lo slave:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/* Embedded Systems Architecture - hardware
 *  application on xx yyy 2019
 *  
 *  serial communication; remote blink 1
 *
 *  this is slave; it execute commands:
 *    - 'a'        leds 0 0
 *    - 'b'        leds 0 1
 *    - 'c'        leds 1 0
 *    - 'd'        leds 1 1
*/

#define LED1   2  //port to drive led
#define LED2   7  //port to drive led

void setup() {
    pinMode(LED1, OUTPUT);
    pinMode(LED2, OUTPUT);
    Serial.begin(9600);
    //Serial.write("START\n");
}

void loop() {
    if(Serial.available()){
        char c = Serial.read();
        if(c=='a'){
            digitalWrite(LED1, LOW);
            digitalWrite(LED2, LOW);
        }
        if(c=='b'){
            digitalWrite(LED1, HIGH);
            digitalWrite(LED2, LOW);
        }
        if(c=='c'){
            digitalWrite(LED1, LOW);
            digitalWrite(LED2, HIGH);
        }
        if(c=='d'){
            digitalWrite(LED1, HIGH);
            digitalWrite(LED2, HIGH);
        }
        Serial.write(c);
    }
  delay(100);
}

La novità consiste nel controllare alla ln 24 se vi è qualcosa nella seriale. In caso di risposta affermativa, leggiamo un carattere (il ns comando) e accendiamo/spegniamo di conseguenza i due LED.

Eseguito il comando, alla ln 42 rimbalziamo indietro il carattere ricevuto.

Dopo una pausa di un decimo di secondo (ln 44) si rientra in loop.

Invece il master è più articolato:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
/* Embedded Systems Architecture - hardware
 *  application on xx yyy 2019
 *  
 *  serial communication; remote blink 1
 *
 *  this is master; it sends commands:
 *    - 'a'        leds 0 0
 *    - 'b'        leds 0 1
 *    - 'c'        leds 1 0
 *    - 'd'        leds 1 1
 */

#define buttonSwitch1    2
#define buttonSwitch2    7

int statusSwitch1;
int statusSwitch2;

void setup() {
    pinMode(buttonSwitch1, INPUT);
    pinMode(buttonSwitch2, INPUT);
    Serial.begin(9600);
    Serial1.begin(9600);
    while(!Serial1){;}
    statusSwitch1 = digitalRead(buttonSwitch1);
    statusSwitch2 = digitalRead(buttonSwitch2);
    sendSwitches(statusSwitch1, statusSwitch2);
}

void loop() {
    int newSwitch1;
    int newSwitch2;
    newSwitch1 = digitalRead(buttonSwitch1);
    newSwitch2 = digitalRead(buttonSwitch2);
    //Serial.print(newSwitch1);
    //Serial.print(", ");
    //Serial.println(newSwitch2);
    if (newSwitch1 != statusSwitch1 || newSwitch2 != statusSwitch2)
        sendSwitches(newSwitch1, newSwitch2);
    delay(100);
}

void sendSwitches(int sw1, int sw2){
    char cmd;
    char resp;
    if (sw1 == 0 && sw2 == 0) cmd = 'a';
    if (sw1 == 0 && sw2 == 1) cmd = 'b';
    if (sw1 == 1 && sw2 == 0) cmd = 'c';
    if (sw1 == 1 && sw2 == 1) cmd = 'd';
    Serial1.write(cmd);
    delay(10);
    while(!Serial1.available()){;}
    resp = Serial1.read();
    Serial.print("sending ");
    Serial.print(cmd);
    Serial.print("; receiving ");
    Serial.println(resp);
    statusSwitch1 = sw1;
    statusSwitch2 = sw2;
}

In questo caso nel setup inizializziamo due seriali: la default (Serial) verso il PC e la Serial1 verso lo slave (ln 22 e 23).

Quindi rimaniamo in attesa sia disponibile la seriale verso lo slave (linea 24: il ciclo while(!Serial) con istruzione nop), dopo di che leggiamo lo stato dei pulsanti e lo inviamo a slave tramite la funzione sendSwitches.

Quest’ultima (ln da 43 a 60), sempicemente controlla lo stato dei pulsanti passati come parametri, e assegna di conseguenza il comando:

  • carattere a se entrambi ad off;

  • carattere b se il primo ad off e il secondo on, e così via.

Dopo di che invia il comando allo slave (ln 50), attende per stabilizzare il segnale, e si mette in attesa del ritorno (ln 52). Quando la seriale invia l’echo, lo manda al PC insieme al comando trasmesso (ln da 54 a 57). Infine salva lo stato dei pulsanti e rientra.

Il loop a questo punto è banale. Basta leggere lo stato dei pulsanti. Se è cambiato rispetto il precedente lo inviamo allo slave, altrimenti: ritardo e cicliamo.

Questi programmi sono tutt’altro che a prova di bomba. Ad es. se lo slave perde un carattere, il master si può inchiodare alla ln 52 in attesa di una risposta che non arriverà mai.

Comunque, ringraziamo il cielo di non dover analizzare l’andamento fisico del segnale sulle linee Tx/Rx: è già molto. E il prof. Culmone ci insegnerà come implementare programmi più robusti … :-)