Skip to the content.

Torna all’indice>simulatore con protothread

SIMULATORE PROTOCOLLO ALOHA PER ARDUINO E TINKERCAD CON ASYNC/AWAIT

Aloha

Utilizzo

1) Premere il tasto codice in alto a destra per aprire la finestra del codice 2) Premere il tasto monitor seriale in basso sulla finestra del codice 3) Selezionare dal menu a tendina in alto a destra il dispositivo di cui si vuole vsualizzare il codice 4) Avviare la simulazione con il tasto apposito in alto a destra 5) Osservare i messaggi di log nella finestra del monitor serial in basso a destra - Se si osserva il monitor dell’arduino N.1 si ritrova in output il log del ricevitore che informa sull’arrivo di un nuovo messaggio dati e l’nvito a premere il pulsante ackBtn per inviare un nuovo messaggio di controllo di ack al trasmettitore. - Se si osserva il monitor dell’arduino N.2 si ritrova in output il log del trasmettitore che informa sull’arrivo di un nuovo messaggio dati e l’nvito a premere il pulsante msgBtn per inviare un nuovo messaggio dati al ricevitore.

Simulazione in tinkercad: https://www.tinkercad.com/embed/0tqncZLsJ3k?editbtn=1

Descrizione schema

Il collegamento tra i due dispositivi in realtà non avviene tramite un mezzo a BUS ma tramite un collegamento full duplex realizzato con i due link di una porta seriale.

Arduino Uno però possiede una sola porta seriale UART che è utilizzata in genere per il monitoring del dispositivo cioè per l’invio di messaggi dal PC al dispositivo e per la ricezione sul PC di messaggi provenienti dal dispositivo.

Si è quindi usata la libreria SoftwareSerial per emulare un collegamento seriale tra due dispositivi impiegando due porte digitali per la trasmissione nei due versi uplink e downlink impostate tramite:

#define rxPin 2
#define txPin 3

Un tasto (Msg TxBtn) sul trasmettitore abilita la trasmissione di un messaggio verso il ricevitore.

txlog.png

Un tasto (Ack TxBtn) sul ricevitore abilita la trasmissione di un ack verso il trasmettitore. La mancata ricezione dell’ack è interpretata dal trasmettitore come una valutazioe indiretta di collisione e quindi causa la ripetizione della trasmissione del messaggio.

rxlog.png

Un blink su un led per ogni scheda indica l’avvenuta trasmissione del messaggio o dell’ack.

Formato dei messaggi

Il formato del messaggio è uniforme per messaggi di ack o dati ed è composto da 5 campi di lunghezza fissa di un byte più un sesto campo finale di lunghezza variabile che contiene il data da trasmettere.

Il messaggio da trasmettere è memorizzato in una coda di trasmissione di 64 byte che viene svuotata e riutilizzata per ogni nuovo messaggio da trasmettere.

Il messaggio ricevuto è memorizzato in una coda di ricezione di 64 byte che viene svuotata e riutilizzata per ogni nuovo messaggio ricevuto.

Nella presente implementazione del protocollo Aloha le due code coincidono per non si può ricevere un messaggio mentre se ne trasmette un’altro.

I campi del messaggio sono:

I messaggi dati hanno nel campo I il valore MSG (55) e hanno un BYTE_CNT variabile maggiore di 5. il log Arrived: (:2),(:1),(:1),(7:55),( :11),(c:99),(i:105),(a:97),(o:111),(p:112),(:0), pay: 6-11 indica che il TX 2 ha inviato al RX 1 del gruppo 1 un messaggio 55 (dati) che contiene la stringa “ciaop” ed avente 11 byte complessivi di cui 6 fissi.

I messaggi di controllo ack hanno nel campo I il valore ACK (129) e hanno un BYTE_CNT sempre di 5. Il log Arrived: (:1),(:2),(:1),(:129),(:5),Ricevuto ack: indica che il TX 1 ha inviato al RX 2 del gruppo 1 un messaggio 129 (ack) di 5 byte complessivi sempre fissi.

Fasi ALOHA

Una stazione trasmittente:

Programmazione sequenziale

L’algoritmo è realizzato in maniera sequenziale nel tempo pianificando il blocco del flusso di esecuzione di parti del codice per un certo tempo di attesa. Il tempo di attesa potrebbe essere:

In entrambi i casi il flusso di esecuzione rimane in attesa della valutazione periodica (polling) di un flag che rappresenta l’accadere di un evento, il timeout di un timer preimpostato nel caso del Wait(t), l’arrivo di un messaggio nel caso del WaitUntil(pollingInput).

Eventi di input

Gli eventi di input rilevanti che potrebbero essere valutati durante lo svolgimento dell’algoritomo sono:

Thread di ricezione e trasmissione

In realtà, il polling degli eventi è fatto all’interno di un’unica funzione chiamata poll() che viene periodicamente richiamata all’interno del loop principale del sistema:

void loop() 
{
	poll(&rxobj);
	.............
}

La poll()modifica delle variabili globali con funzione di indicazione di evento dette flag (bandierine) che sono specifiche per ogni evento e che vengono valutate all’interno dei thread del sistema (i thread condividono le stesse variabili globali).

I thread rappresentano blocchi di codice la cui esecuzione si svolge in maniera parallela ed indipendente e, per uno stesso dispositivo fisico, sono essenzialmente due: un thread di ricezione ed un thread di trasmissione.

Arduino non possiede capacità di multithreading reale di tipo preemptive (competitvo con prerilascio) e realizzato in HW, ciònonostante è comunque possibile realizzare in SW una schedulazione di tipo non preemptive (collaborativa senza prerilascio) utilizzando i protothread.

Ricevitore

Definizione delle variabili con il ruolo di descrittore degli stati asincroni e prototipi delle funzioni asincrone (necessari perchè il compilatore di Tinkercad li richiede in quanto non riordina casualmente la definizione dei tipi):

as_state ptRcv, ptSend;
async rcvTask(as_state *pt);
async sendTask(as_state *pt);

Protocollo ALOHA in ricezione pseudocodice:

While(true){
	WaitUntil(dataFrameArrived());
	if(!duplicate()){ 
		deliver(frame) 
	}
	send(ack_frame);
}

Definizione del thread di ricezione messaggio

async rcvTask(as_state *pt) {
  async_begin(pt);
  // Loop forever
  while(true) {
	digitalWrite(led, HIGH);
	await_delay(50);
	digitalWrite(led, LOW);
	await_delay(50);	
	await(dataFrameArrived());
	rcvEventCallback();
	Serial.println("Premi il tasto per trasmettere un ack.");
	await(digitalRead(ackBtn));
	sendAck();  
  }
  async_end;
}

Trasmettitore

Definizione delle variabili con il ruolo di descrittore degli stati asincroni e prototipi delle funzioni asincrone (necessari perchè il compilatore di Tinkercad li richiede in quanto non riordina casualmente la definizione dei tipi):

as_state ptRcv;
async rcvTask(as_state *pt);

Protocollo ALOHA in trasmissione in pseudocodice:

N=1;
while(N <= max){
	send(data_frame);
	waitUntil(ackOrTimeout());
	if(ack_received){
		exit while;
	}else{
		/* timeout scaduto: ritrasmissione*/
		t=random()*WNDW*2^n;
		wait(t);
		N=N+1;
}
/* troppi tentativi: rinuncio a trasmette

Definizione del thread di trasmissione messaggio

async sendTask(as_state *pt) {
  async_begin(pt);
  // Loop forever
  while(true) {
	while(n < MAXATTEMPTS){
		sendData(&txobj);
		Serial.println("Attendo ack o timeout: ");
		await(ackOrTimeout());
		if(ack_received()){
			n = MAXATTEMPTS;
			Serial.println("Ricevuto ack: ");
		}else{
			Serial.print("Timeout n: ");
			Serial.print(n);
			Serial.print(": ritrasmissione tra: ");
			tt = getBackoff();
			Serial.print((float) tt/1000);
			Serial.println(" secondi");
			/* timeout scaduto: ritrasmissione*/
			await_delay(tt);
			Serial.print(" Ritrasmesso.");
			n++;			
		}
	 }
	 Serial.println("Premi il tasto per trasmettere un messaggio.");
	 await(digitalRead(txBtn));
	 digitalWrite(led, HIGH);
	 await_delay(50);
	 digitalWrite(led, LOW);
	 await_delay(50);	
	 n = 0;  //azzera conteggio
  }
  async_end;
}

Backoff a finestra variabile

L’algoritmo ALOHA proposto utilizza un backoff a finestra variabile in cui la finestra di trasmissione aumenta esponenzialmente (con la potenza del 2) ad ogni incremento del numero di tentativi andati a vuoto. La finestra di trasmissione è detta anche finestra di contesa e rappresenta l’intervallo temporale in cui ogni stazione calcola individualmente un tempo random di accesso a partire dal quale cominciare a trasmettere.

long getBackoff(){
	return random(0, WNDW*pow(2,n));
}

Torna all’indice>simulatore con protothread