Elaborato Parricelli Francesco N46001992
Transcript
Elaborato Parricelli Francesco N46001992
Scuola Politecnica e delle Scienze di Base Corso di Laurea in Ingegneria Informatica Elaborato finale in Calcolatori Elettronici I Gestione dei flussi dati in una rete di sensori basata su piattaforma Arduino Anno Accademico 2015/2016 Candidato: Francesco Parricelli matr. N46/001992 Alla Mia dolce metà, Alla mia famiglia, Ai miei amici. Indice Prefazione e Ringraziamenti ix 1 Introduzione ai Sistemi Embedded 1 1.1 Principi generali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2 Componenti dei sistemi embedded . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.2.1 Componenti comuni . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.2.2 Componenti specifici . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.3 Classificazione dei sistemi embedded . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3 1.4 Performance nei sistemi embedded . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5 1.4.1 5 Requisiti di sviluppo e Scelte di Design . . . . . . . . . . . . . . . . . . . . . . . . 2 La piattaforma Arduino 2.1 2.2 2.3 7 Cenni generali . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 2.1.1 8 Arduino IDE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Aspetti Hardware della piattaforma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.2.1 Arduino Mega 2560 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.2.2 Arduino Ethernet Shield 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.2.3 Arduino GSM Shield 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.2.4 WiFi Module - ESP8266 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.2.5 Transceiver RS485 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.2.6 Arduino UNO . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.2.7 Display compatibile Hitachi HD44780 e Keypad numerico . . . . . . . . . . . . . . 16 Prototipo di debugging . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16 3 Descrizione e sviluppo del progetto 3.1 Panoramica . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 3.1.1 3.2 17 Componenti del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17 Soluzioni di sviluppo del sistema . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18 3.2.1 Sviluppo con Protocollo RS485 - Architettura semplificata . . . . . . . . . . . . . . 19 3.2.2 Sviluppo con Protocollo RS485 - Architettura raffinata . . . . . . . . . . . . . . . . 27 3.2.3 Sviluppo con Protocollo TCP/IP - Ethernet . . . . . . . . . . . . . . . . . . . . . . 30 iv 3.2.4 3.3 Sviluppo con Protocollo TCP/IP - WiFi . . . . . . . . . . . . . . . . . . . . . . . . 36 Scheduling delle Richieste . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 3.3.1 Design dello scheduler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 41 3.3.2 Esempio di utilizzo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 4 Conclusioni 45 Riferimenti 46 v Elenco delle tabelle 1.1 Requisiti di Design per Sistemi Embedded . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1 Tabella riassuntiva ATmega2560 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 vi 6 Elenco delle figure 1.1 Esempio di Sistema di ABS, adattato da [1] . . . . . . . . . . . . . . . . . . . . . . . . . . 1 1.2 Schema generico di un sistema embedded . . . . . . . . . . . . . . . . . . . . . . . . . . . 2 1.3 Architettura di un sistema embedded . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4 2.1 Componenti fondamentali architettura Arduino . . . . . . . . . . . . . . . . . . . . . . . . 7 2.2 Arduino IDE . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8 2.3 Arduino Board Mega 2560 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9 2.4 Atmel ATmega2560 - Diagrammi dei blocchi, adattato da [6] . . . . . . . . . . . . . . . . 10 2.5 Arduino Ethernet Shield 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.6 Wiznet W5500 - Schema a blocchi, adattato da [7] . . . . . . . . . . . . . . . . . . . . . . 12 2.7 Arduino GSM Shield 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12 2.8 Schema Quectel M10, adottato da [8] . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.9 Schema a blocchi ESP8266, adottato da [9] . . . . . . . . . . . . . . . . . . . . . . . . . . 13 2.10 Configurazione dei Pin - SN75HVD08, adottato da [10] . . . . . . . . . . . . . . . . . . . . 14 2.11 Arduino UNO - Board . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.12 Arduino UNO - Diagramma a blocchi . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15 2.13 Snapshot del prototipo di testing e debugging . . . . . . . . . . . . . . . . . . . . . . . . . 16 3.1 Schema Architetturale realizzativo del sistema - adattato da [12] . . . . . . . . . . . . . . 18 3.2 Schema Architetturale Semplificativo del sistema - adattato da [12] . . . . . . . . . . . . . 19 3.3 Modalità di Indirizzamento adottata . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20 3.4 Estratto del file di intestazione: mappingStructure.h . . . . . . . . . . . . . . . . . . . . 21 3.5 Estratto dello sketch Centrale.ino relativo allo sviluppo con protocollo RS485 . . . . . . 22 3.6 Estratto dello sketch Gateway.ino relativo allo sviluppo con protocollo RS485 . . . . . . 23 3.7 Estratto dello sketch Sensore.ino relativo allo sviluppo con protocollo RS485 . . . . . . 25 3.8 Scambio indirizzo tra Centrale (a sinistra) e Gateway (a destra) . . . . . . . . . . . . . . . 25 3.9 Scambio valori dei sensori tra Centrale (a sinistra) e Gateway (a destra) . . . . . . . . . . 26 3.10 Comunicazione tra Gateway (a sinistra) e Concentratore (a destra) . . . . . . . . . . . . . 26 3.11 Esempio di testing della piattaforma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27 3.12 Schema Architetturale Raffinato del sistema - adattato da [12] . . . . . . . . . . . . . . . 28 3.13 Modalità di Indirizzamento a tre livelli . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28 vii 3.14 Estratto dello sketch Concentratore.ino relativo allo sviluppo con protocollo RS485 Raffinato . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30 3.15 Comunicazione tra Gateway (a sinistra) e Concentratore (di primo livello) (a destra) . . . 31 3.16 Estratto dello sketch Centrale.ino relativo allo sviluppo con protocollo Ethernet . . . . 32 3.17 Estratto dello sketch Gateway.ino relativo allo sviluppo con protocollo Ethernet . . . . . 33 3.18 Estratto dello sketch Concentratore.ino relativo allo sviluppo con protocollo Ethernet . 34 3.19 Comunicazione tra Centrale (a sinistra) e Gateway (a destra) con Ethernet . . . . . . . . 35 3.20 Estratto dello sketch Concentratore.ino relativo alla funzione ReadMySensor() - Ethernet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36 3.21 Esempio di testing della piattaforma con Ethernet . . . . . . . . . . . . . . . . . . . . . . 36 3.22 Estratto dello sketch Centrale.ino relativo allo sviluppo con WiFi . . . . . . . . . . . . . 37 3.23 Estratto dello sketch Centrale.ino relativamente alle funzioni SendRequest() (a sinistra) e pollingToGateway() (a destra) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38 3.24 Implementazione della procedura sendData() . . . . . . . . . . . . . . . . . . . . . . . . . 39 3.25 Modalità di invio dati, estratto dallo sketch Concentratore.ino . . . . . . . . . . . . . . 39 3.26 Estratto dello sketch Concentratore.ino relativo alla funzione readMySensor() - WiFi 40 3.27 Esempio di testing della piattaforma con WiFi . . . . . . . . . . . . . . . . . . . . . . . . 41 3.28 Estratto del file myScheduler.h - Descrittore del task (a sinistra) e Struttura dello scheduler (a destra) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42 3.29 Estratto del file myScheduler.h - Procedure di Utility dello Scheduler . . . . . . . . . . . 42 3.30 Estratto dello sketch (inizializzazione) Gateway.ino utilizzato come centrale nel testing dello scheduler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43 3.31 Estratto dello sketch Gateway.ino utilizzato come centrale nel testing dello scheduler . . 43 3.32 Screenshot del monitor seriale del Gateway utilizzato come centrale . . . . . . . . . . . . . 44 viii Prefazione e Ringraziamenti Il presente elaborato si pone l’obiettivo di sviluppare un sistema di gestione di dati sensoriali attraverso la piattaforma Arduino. In particolare, al fine di poter definire un progetto così descritto, risulta necessario conoscere i dispositivi utilizzati, in termini di caratteristiche e funzionalità. A tal proposito, l’elaborato si suddivide in tre parti, corrispondenti ai primi tre capitoli, sinteticamente presentati di seguito: • il primo capitolo si occupa di fornire una panoramica introduttiva circa i sistemi embedded da un punto di vista hardware e software, caratterizzando le differenze rispetto ai classici e comuni sistemi general-purpose riportando esempi celebri di applicazione degli stessi. • il secondo capitolo introduce la piattaforma Arduino, soffermandosi da un punto di vista hardware sui componenti base e addizionali della stessa, presentando, infine, un prototipo fisico di sviluppo utilizzato nel testing del progetto realizzato. • il terzo capitolo è il cuore dell’elaborato e descrive l’architettura del sistema, gli aspetti implementativi, le scelte di design ed i motivi ad esse correlate. I primi due capitoli sono, quindi, rivolti a fornire uno strato teorico di base, necessario a comprendere le scelte di progetto adoperate nello sviluppo del sistema. La presentazione del progetto da sviluppare è condotta seguendo un approccio step-by-step aumentando, gradualmente, la complessità di realizzazione, in accordo al risultato finale da ottenere. Prima di proseguire, è doveroso porgere un ringraziamento al Professore Alessandro Cilardo per l’opportunità da egli offertami di operare attivamente nel settore dei sistemi embedded e per il supporto fornitomi durante lo sviluppo di questo elaborato. ix Capitolo 1 Introduzione ai Sistemi Embedded 1.1 Principi generali Che cos’è un sistema embedded? A tal riguardo si possono fornire differenti definizioni. Tipicamente, in una maniera fortemente semplicistica, si può definire un sistema embedded come un qualunque dispositivo che include un computer programmabile, il cui utilizzo non è inteso ad essere di tipo general purpose [1]. Procedendo in una direzione di maggior dettaglio, un sistema embedded lo possiamo descrivere come una combinazione di componenti hardware e software, ed eventualmente periferiche addizionali, nonché meccanismi elettronici e meccanici, atti a definire una funzione dedicata [2]. È evidente che, quindi, mentre i comuni calcolatori, tipicamente commerciali, sono predisposti ad un utilizzo generico, questa particolare classe di sistemi è invece dedicata all’esecuzione di una funzionalità specifica. Altresì, è fondamentale chiarire che, alla luce di quanto evidenziato, un normale calcolatore può essere utilizzato per la costruzione di un sistema integrato, programmandolo in maniera opportuna. Frequentemente, un sistema embedded è un componente incluso in un generico sistema più grande. A tal proposito possiamo illustrare differenti esempi; se consideriamo il settore automotive, si può facilmente corrispondere alla visione dell’autoveicolo come un insieme di sistemi integrati che definiscono ciascuno una differente funzionalità, e che concorrono nell’obiettivo di controllo del mezzo. In particolar modo la figura 1.1 illustra il componente ABS1 . Figura 1.1. Esempio di Sistema di ABS, adattato da [1] 1 Antilocking Braking System, è un sistema di sicurezza che evita il bloccaggio delle ruote dei veicoli garantendone la guidabilità durante le frenate 1 1 – Introduzione ai Sistemi Embedded Può essere, inoltre, interessante notare che un computer general-purpose si interfaccia a diversi sistemi integrati: esempi banali possono corrispondere alla tastiera, al mouse e alla stampante. L’esistenza di un processore e di uno strato software all’interno di un sistema embedded può non essere ravvisato da un utente utilizzatore. In alcuni casi ciò può corrispondere alla realtà, in quanto si preferisce, piuttosto che dotare il device della coppia software-processore, progettare una circuiteria integrata dedicata all’esecuzione della funzione del sistema in hardware. Tuttavia, è utile osservare che il primo approccio risulta essere più flessibile, talvolta anche meno costoso e più semplice, rispetto alla definizione di un circuito integrato (IC). 1.2 1.2.1 Componenti dei sistemi embedded Componenti comuni Così come osservato nel paragrafo 1.1, secondo una definizione non-hardwired, si può stabilire che ogni sistema integrato contenga un processore e un software [2]. Tali componenti rappresentano, dunque, delle features comuni per essi. È utile osservare, che affinché un sistema di questo tipo possa eseguire un certo programma è necessario che tale sia provvisto di un’area ove memorizzare il codice eseguibile, e un’area ove memorizzare temporaneamente dati relativamente all’esecuzione dello stesso. Per tale motivo è necessario che ogni dispositivo embedded abbia a disposizione una memoria a sola lettura (ROM, Read Only Memory) e una memoria RAM (Random Access Memory). Per sistemi integrati su piccola scala, per i quali necessita solo una memoria di piccole dimensioni, essa è, tipicamente, immersa all’interno dello stesso chip del processore. Figura 1.2. Schema generico di un sistema embedded Tutti i sistemi embedded reagiscono ad un insieme di dati d’ingresso con una o più azioni di uscita. Ad esempio consideriamo il sistema di antibloccaggio delle ruote in frenata (ABS) in figura 1.1. In questo caso, su ogni ruota del veicolo è posto un Encoder, formato da un trasduttore e da una ruota fonica, che è costituita da una ruota dentata simile ad un ingranaggio che gira con la ruota del veicolo ed un sensore di prossimità induttivo fisso che rileva il passaggio dei denti di suddetta ruota. La centralina elettronica, contando il numero di denti che passano in una data unità di tempo, calcola la velocità di rotazione della ruota e se rileva che una o più ruote sono bloccate in fase di frenata comanda la pompa idraulica in modo da diminuire la forza di frenata, evitando il bloccaggio [13]. 2 1 – Introduzione ai Sistemi Embedded 1.2.2 Componenti specifici Ad eccezione di quanto appena specificato, un sistema embedded lo si può caratterizzare da una circuiteria hardware esclusiva, su cui esegue un software esclusivo. Sia la struttura software che hardware sono strettamente legate al campo di utilizzo, e, dunque, dalla funzione da esso implementata; ad esempio, un sistema di controllo di un aeromobile necessita di uno strato operativo real-time, al fine di assicurare il soddisfacimento dei vincoli temporali. Nel tempo, la richiesta di tali sistemi è stata spesso accompagnata dall’esigenza di definire dei dispositivi sempre di dimensioni minori. In tale ottica, la nascita dei microcontrollori ha permesso di conseguire un risultato ottimo, rispetto ad un punto di vista dimensionale, fornendo, allo stesso tempo, un elevata potenza, in termini di capacità di calcolo. A questo proposito è importante sottolineare la differenza tra microprocessore e microcontrollore; in particolare, il primo è un singolo chip VLSI2 che include solo la logica di elaborazione mentre richiede sempre delle unità esterne per poter scambiare informazioni e interagire con l’esterno. Il microcontrollore è invece un sistema completo, che integra in uno stesso chip il processore, la memoria permanente, la memoria volatile e i canali (pin) di I/O, oltre ad eventuali altri blocchi specializzati. Seguendo una tale ottica, un sistema embedded lo si può anche associare ad un sistema basato su microprocessore costruito per il controllo di una funzione o un insieme di funzioni [3]. I microcontrollori possono essere classificati secondo differenti livelli di sofisticatezza. Tale classificazione è tipicamente condotta in base alla dimensione della parola (word). Considerando un dettaglio maggiore, i microcontrollori a 8-bit vengono impiegati attraverso altri componenti (ad esempio dispositivi di I/O), in sistemi low-cost; microcontrollori a 16-bit vengono utilizzati in applicazioni più sofisticate. Infine, sono da citare i microprocessori RISC3 a 32-bit, che offrono alte performance per applicazioni CPU-intensive. A seguire, in figura 1.3 è rappresentato un esempio di architettura hardware di un sistema embedded [4]. In virtù di ciò, è necessario, quindi, illustrare l’importanza delle periferiche in un sistema integrato nell’interazione dello stesso con l’ambiente esterno in cui è immerso. Ciò avviene attraverso dispositivi collegati mediante delle porte di comunicazione seriale. Esempi di devices di input possono risultar essere sensori, tastierini numerici e parallelamente devices di output possono identificarsi con display, memorie, output seriali. 1.3 Classificazione dei sistemi embedded Come già accennato, tipicamente, un sistema embedded è esclusivo, nel senso che esso viene progettato e distribuito per l’esecuzione di una funzione dedicata, In tale eterogeneità è possibile definire una classificazione, che suddivide questi sistemi in: 2 Very Large Scale Integration, indica una elevata integrazione di transistor all’interno di un singolo chip 3 L’acronimo RISC, dall’inglese Reduced Instruction Set Computing, indica una filosofia di progettazione di architetture per microprocessori che predilige lo sviluppo di un’architettura semplice e lineare 3 1 – Introduzione ai Sistemi Embedded Figura 1.3. Architettura di un sistema embedded 1. Sistemi a Piccola Scala: si tratta di sistemi dotati di un singolo microcontrollore a 8-bit o 16-bit. Tipicamente si tratta di dispositivi semplici, con hardware poco sofisticato, su cui viene eseguito un software non complesso. Per tale tipo di sistema, vengono messi a disposizione, spesso, dei tool di svliuppo, comprensivi di IDE4 , compilatori e interfacce di programmazione (API); il linguaggio utilizzato per la programmazione dei suddetti devices è, usualmente, il C. Esempi di sistemi embedded di questo tipo sono: • Keyboard Controller; • Controller HDD o CD-Drive; • Telecomando per TV; • Smartphone; • Elettrodomestici (Lavatrici, Forni elettrici, ...). 2. Sistemi a Media Scala: sono sistemi dotati di uno o più microcontrollori a 16-bit o 32-bit. A differenza dei primi, sono più complessi sia da un punto di vista software che hardware. Poiché, dunque, risultano adatti allo sviluppo di applicazioni complesse, tipicamente, lo sviluppo software è guidato dalla presenza di RTOS, simulatori, debugger e ambienti di sviluppo integrati. Anche in questo caso sono presenti delle librerie che semplificano il developing, permettendo di mascherare la complessità circuitale del device. Esempi di sistemi embedded di questo tipo sono: • Computer Network Systems (router, switch, bridge,...); • Sistemi Bancari (ATM); • Smartphone; 4 Integrated Developing Envivorment, ambiente di sviluppo integrato 4 1 – Introduzione ai Sistemi Embedded • Sistemi di Intrattenimento (Video Games, ...). Sistemi Sofisticati: sono sistemi fortemente sofisticati, con enormi complessità hardware e 3. software, che possono avere la necessità di utilizzare processori configurabili o scalabili. É possibile che tali sistemi prevedano la definizione di alcune funzionalità complesse, come ad esempio la DCT, Discrete Cosine Transformation, a livello hardware, attraverso una circuiteria dedicata, al fine di migliorare le prestazioni. Anche in questo caso sono messe a disposizione dello sviluppatore API e strumenti di supporto. Esempi di sistemi embedded di questo tipo sono: • Sistemi Embedded per la wireless LAN; • Sistemi Embedded per il video Real-Time; • Prodotti per la sicurezza; • Sistemi di Network Security. 1.4 Performance nei sistemi embedded Il concetto di performance è un qualcosa di relativo allo specifico ambito di applicazione del sistema. A titolo di esempio, in un comune PC, general-purpose, si vuole ridurre al minimo il tempo medio di risposta dei task, al fine di ottenere un buon compromesso circa la reattività del sistema. Ciò non è, invece, vero per il campo dei sistemi integrati. Per comprendere ciò, consideriamo il cuore di questa classe di sistemi, ossia il settore real-time. I sistemi in tempo reale sono quei sistemi di calcolo per i quali la correttezza di funzionamento non dipende solo dalla validità dei risultati ottenuti, ma anche dal tempo in cui tali risultati sono prodotti[5]. La caratteristica di questi sistemi è la presenza di una deadline, che rappresenta, per un certo task (o istanza di esso) il tempo massimo entro cui tale deve terminare la propria esecuzione. A seconda della classe di criticità, un superamento di deadline può invalidare l’intero sistema o definire un degradamento della QoS (Quality of Service). È evidente, quindi, che, per questi sistemi non ha senso minimizzare il tempo medio di risposta delle applicazioni; risulta, invece, essenziale, garantire che ogni task possa terminare entro il proprio limite massimo temporale. Ciò viene ottenuto attraverso l’utilizzo di tecniche di scheduling appositamente definite per garantire il rispetto delle deadline. 1.4.1 Requisiti di sviluppo e Scelte di Design Sulla base di quanto detto, è evidente che ad ogni singolo sistema integrato debba corrispondere ad uno specifico set di requisiti. È importante notare che, spesso, il soddisfacimento di questi requisiti, contemporaneamente, non è possibile, e per tale motivo risulta necessario definire un trade-off, ossia un compromesso, antecedente lo sviluppo del prodotto. Ad esempio, un tipico vincolo che spesso occorre è il budget a disposizione; in questo caso il soddisfacimento di altri requisiti è, ovviamente, subordinato al suddetto. Si specifica che tale non rappresenta l’unico possibile vincolo che può presentarsi all’atto di sviluppo di questa classe di prodotti. A seguire vengono descritti altri possibili requisiti. 5 1 – Introduzione ai Sistemi Embedded • Potenza di calcolo: rappresenta la massima domanda di calcolo che il chip riesce a gestire. Tipicamente, un metodo utilizzato per la valutazione della potenza di calcolo è il fattore MIPS, Million Instructions Per Second, rappresentante il numero (in ordine 106 ) di istruzioni che vengono eseguite in un secondo dal chip. Ovviamente, il MIPS non è l’unico fattore che influenza la potenza di calcolo; si fa riferimento ad altre features, quali, ad esempio, la dimensione dei registri del processore: ad oggi i sistemi embedded vengono costruiti con processori, tipicamente, a 16-bit. • Storage: intesa come la quantità di memoria richiesta per l’archiviazione del software da eseguire e dei dati da esso manipolati. Si nota come la quantità di memoria a disposizione influenzi anche la scelta del processore. Infatti un processore a 16-bit può indicizzare fino a 216 locazioni di memoria. • Numero di unità: rappresenta il numero di unità di produzione del prodotto atteso. Tale fattore influenza in particolar modo il budget a disposizione per lo sviluppo. • Consumo di energia: è la quantità di energia richiesta per l’elaborazione, di particolare importanza per i dispositivi alimentati da batteria. Ovviamente, si cerca sempre di ottenere un basso consumo, al fine di aumentare l’autonomia energetica del dispositivo, ma anche limitare le dimensioni e il riscaldamento dello stesso. • Tempo di Vita: rappresenta il tempo atteso di utilizzo del dispositivo. Ciò va a influenzare la scelta dei componenti hardware e, dunque, il costo di sviluppo del progetto. • Affidabilità: rappresenta quanto deve essere affidabile il prodotto. Ad esempio un giocattolo può anche non funzionare il 100 percento delle volte, mentre se l’ABS di un auto ha un malfunzionamento, ciò può arrecare danno a persone o cose (sistema safety-critical). In tabella 1.1 è presente uno schema sintetico di quanto specificato. Requisito/Criterio Basso Medio Alto Processore 4-bit o 8-bit 16-bit 32-bit o 64-bit Memoria <64KB da 64KB a 1MB >1MB Costo di Sviluppo <$100.000 da $100K a $1M >$1M Costo di Produzione <$10 da $10 a $1000 >$1000 Numero di Unità <100 da 100 a 10000 >10000 Consumo di Energia >10mW/MIPS da 1 a 10mW/MIPS <1mW/MIPS Tempo di Vita Giorni, settimane o mesi Anni Decadi Affidabilità Può occasionalmente fallire Deve essere affidabile Non deve fallire Tabella 1.1. Requisiti di Design per Sistemi Embedded 6 Capitolo 2 La piattaforma Arduino 2.1 Cenni generali Arduino è una piattaforma prototipale di sviluppo open-source, caratterizzata dalla presenza di una semplice board di input/output. Questa si basa su un circuito stampato che integra un microcontrollore con pin connessi alle porte I/O, un regolatore di tensione e quando necessario un’interfaccia USB che permette la comunicazione con il computer. A questo hardware viene affiancato un ambiente di sviluppo integrato (IDE) multipiattaforma (per Linux, Apple MacOS e Windows). Tale ambiente permette di creare dei programmi (sketch) ed eseguirli sulla piattaforma. (a) Arduino PRIMO Board Figura 2.1. (b) Arduino NFC Shield Componenti fondamentali architettura Arduino L’hardware Arduino originale viene prodotto in Italia dalla Smart Project, una società di proprietà di uno dei co-fondatori del progetto. Ad oggi, sono messe a disposizione differenti tipologie di schede, ognuna delle quali rientra in una determinata categoria di utilizzo, similmente a quanto specificato nel paragrafo 1.3. Tali schede sono dotate, tipicamente, di un microcontrollore Atmel; sono presenti in realtà anche schede dotate di microcontrollori STMicroelectronics o Nordic. In generale i chip Atmel sono progettati secondo l’architettura AVR, mentre i chip Nordic o STMicroelectronics secondo l’architettura ARM-Cortex. In particolare la definizione di un prototipo Arduino necessita della presenza di una board, che rappresenta 7 2 – La piattaforma Arduino il blocco fondamentale di sviluppo per il progetto, caratterizzato da un microcontrollore e altre features a seconda della classe. Ad esempio in figura 2.1(a) è rappresentata una board Arduino Primo. Arduino Primo include la presenza di un microcontrollore Nordic nRF52832, di un modulo ESP8266 per la comunicazione WiFi, un insieme di sensori e un caricatore di batteria. Il microcontrollore Nordic include l’NFC, e Bluetooth Smart. Per fornire delle features aggiuntive alla board è possibile utilizzare le Shield. Arduino mette a disposizione un grosso numero di shields, a partire dall’Ethernet Shield fino ad arrivare all’NFC, in figura 2.1(b), e GSM Shield. 2.1.1 Arduino IDE Come già accennato, Arduino mette a disposizione per lo sviluppo, un ambiente integrato, Arduino IDE, costituito da un editor di testo, un’area messaggi, una console testuale, una toolbar con funzioni comuni e un menu. Tramite questo ambiente è possibile collegarsi direttamente alla board, effettuare l’upload dello sketch ed eseguirlo. Figura 2.2. Arduino IDE In figura 2.2 è illustrato uno screenshot del tool. Si nota che la definizione dello sketch, in ambiente Arduino, è caratterizzato da due procedure fondamentali: 8 2 – La piattaforma Arduino 1. void setup(), che contiene il codice di inizializzazione della board, che viene eseguito una sola volta, all’inizio. 2. void loop(), che contiene il codice relativo alle azioni da effettuare, eseguito ciclicamente dalla board. 2.2 Aspetti Hardware della piattaforma Poiché nel prosieguo dell’elaborato, così come specificato nella prefazione, sarà presentato un progetto di sviluppo prototipale con la piattaforma Arduino, è necessario riportare le caratteristiche delle schede utilizzate, al fine di comprendere le scelte di progetto operate. Senza entrare nel dettaglio, si è scelto di utilizzare come prototipo una piattaforma costituita da una board Arduino Mega 2560, uno shield Arduino Ethernet Shield 2, un ulteriore shield Arduino GSM Shield 2, un modulo WiFi ESP8266, e infine una scheda con transceiver RS485. Si aggiunge inoltre, in ottica di debugging, la presenza di una ulteriore board Arduino UNO, di un display LCD compatibile HD44780, a 4 righe e 20 colonne e infine di un keypad numerico di dimensioni 4x3. Di seguito si dettagliano le caratteristiche hardware dei componenti sopra elencati. 2.2.1 Arduino Mega 2560 Arduino Mega 2560 è una board basata sul microcontrollore Atmel ATmega2560. È dotato di 54 pin digitali di input/output di cui 15 possono essere utilizzati come output PWM1 , 16 input analogici, 4 UART2 , un clock a 16 MHz, una interfaccia USB e un jack di alimentazione. Questa board differisce dai modelli Arduino Mega precedenti in quanto utilizza come convertitore USB-to-serial un ATmega16U2. In particolare la Revision 3 possiede anche pin per la comunicazione I2C, SDA e SCL. Figura 2.3. Arduino Board Mega 2560 1 Pulse Width Modulation, è una tecnica per ottenere un risultato analogico attraverso un mezzo digitale. Il controllo digitale è utilizzato per definire un’onda quadra. La tecnica simula il comportamento analogico, ossia tutte le tensioni tra 0 e 5V cambiando la porzione di tempo per cui un segnale è on oppure è off. Definito l’on-time, ossia il tempo in cui il segnale è alto, come pulse width, la modifica di questo valore permette di ottenere valori analogici 2 Porte hardware seriali 9 2 – La piattaforma Arduino Per la programmazione della board, Arduino mette a disposizione delle librerie in virtù di astrarre lo sviluppo dai dettagli hardware del dispositivo. In figura 2.3 è rappresentata la board Arduino Mega 2560. Atmel ATmega2560 L’ ATmega2560 è un microcontrollore CMOS, a 8-bit, facente parte della famiglia AVR, e, dunque basato su una tecnologia RISC. È caratterizzato dalla presenza di 32 registri interni general-purpose, tutti collegati direttamente all’unità logico-aritmetica (ALU), inclusi due registri indipendenti che possono essere acceduti attraverso una singola istruzione eseguita in un solo ciclo di clock. Figura 2.4. Atmel ATmega2560 - Diagrammi dei blocchi, adattato da [6] È presente una memoria flash programmabile da 256KB, una EEPROM da 4KB, una SRAM da 8KB 86 pin di I/O general purpose. Oltre a ciò figurano altre features, come la presenza di Timer, USART3 , porta SPI seriale, una interfaccia seriale byte-oriented 2-wire e 16 canali ADC. 3 Universal Synchronous/Asynchronous Receiver/Transmitter è un chip che facilita la comunicazione tramite una porta seriale utilizzando il protocollo RS-232C. A differenza dell’UART permette una comunicazione sincrona 10 2 – La piattaforma Arduino Device Flash EEPROM SRAM Pin I/O Canali PWM USART Seriali Canali ADC ATmega2560 256KB 4KB 8KB 86 12 4 16 Tabella 2.1. 2.2.2 Tabella riassuntiva ATmega2560 Arduino Ethernet Shield 2 Così come prima accennato, Arduino permette di aggiungere funzionalità alla main board attraverso un approccio modulare tramite l’utilizzo di shield. In particolare, il modulo in questione permette il collegamento alla rete tramite un classico cavo RJ45, e in più supporta il PoE4 , al fine di alimentarsi direttamente dal cavo ethernet. Figura 2.5. Arduino Ethernet Shield 2 In questo caso, ai fini di semplificare lo sviluppo, Arduino mette a disposizione un insieme di API raccolti nella libreria Ethernet2.h. In più, come si nota anche in figura 2.5 è presente uno slot per MicroSD, come supporto di memorizzazione esterna. Il chip utilizzato è il Wiznet W5500. Wiznet W5500 Il chip W5500 è un controllore embedded TCP/IP hardwired, che è tipicamente utilizzato in sistemi embedded per permettere la connessione alla rete Internet. Per tale motivo esso implementa il protocollo TCP/IP e aderisce allo standard Ethernet 802.3 10/100 Mbit/s. In virtù di ciò offre pieno supporto ai protocolli TCP, UDP, IPv4, ICMP, ARP, IGMP, PPPoE. È dotato di un buffer di memoria interna da 32KB per il processing dei pacchetti Ethernet. Il collegamento al microcontrollore avviene attraverso l’interfaccia SPI; a tal riguardo, il chip raggiunge frequenze di 80Mhz al fine di comunicare in maniera veloce con la board. Infine, per il risparmio energetico adotta il power down mode e lo standard WoL (Wake on LAN), che permette di risvegliare un dispositivo remotamente tramite la conoscenza dell’indirizzo MAC. 4 Power over Ethernet, tecnica molto utilizzata al fine di alimentare il dispositivo con lo stesso cavo utilizzato per la rete dati 11 2 – La piattaforma Arduino Figura 2.6. 2.2.3 Wiznet W5500 - Schema a blocchi, adattato da [7] Arduino GSM Shield 2 L’Arduino GSM Shield 2 fornisce alla board la funzionalità di connettersi ad internet tramite la rete wireless GPRS. In particolare questo modulo è dotato di uno slot per SIM Card, in modo tale da permettere al prototipo la connessione alla rete cellulare. È possibile, dunque, anche ricevere/effettuare chiamate (è infatti presente un jack per microfono e speaker esterno) e inviare/ricevere messaggi di testo (SMS). Figura 2.7. Arduino GSM Shield 2 Tale scheda è dotata di un chip Quectel M10. Quectel M10 Il Quectel M10 è una soluzione Quad-band GSM/GPRS che può essere utilizzato in applicazioni embedded. Fornisce il supporto a tecnologie GSM/GPRS 850/900/1800/1900 MHz ed è utilizzabile per voce, SMS, Data Network e Fax. L’M10 supporta una tecnologia chiamata QuecFOTA, che permette un semplice update del firmware tramite il microcontrollore attraverso l’UART. 12 2 – La piattaforma Arduino Figura 2.8. Schema Quectel M10, adottato da [8] In figura 2.8 è presente uno schema a blocchi del chip. Notiamo la presenza di una flash memory e di una RAM statica, oltre al baseband GSM e la parte dedicata alla frequenza radio. Inoltre il chip è dotato di un insieme di interfacce per il collegamento con l’esterno. In particolare si noti la presenza dell’interfaccia UART, la fondamentale presenza dell’interfaccia SIM Card e dell’interfaccia Audio, necessari per poter sfruttare a pieno le potenzialità del chip. 2.2.4 WiFi Module - ESP8266 L’ESP8266 è il nome di un microcontrollore progettato dalla Espressif Systems. È descritto come una soluzione di networking WiFi self-contained che può essere utilizzata sia per permettere il collegamento WiFi ad un pre-esistente microcontrollore, sia come microcontrollore a sé stante. Figura 2.9. Schema a blocchi ESP8266, adottato da [9] Come modulo WiFi aderisce al protocollo standard 802.11 b/g/n. Per tale ragione ha piena compatibilità con i protocolli IPv4, TCP/UDP, supporta la security WPA/WPA2 e come algoritmo crittografico WEP, TKIP e AES. Il modulo può funzionare sia come Access Point, sia come stazione e infine, anche 13 2 – La piattaforma Arduino in entrambe le modalità. La programmazione avviene attraverso l’utilizzo di comandi AT5 . Ad esempio il comando per il restart del modulo è AT+RST. Ad ogni comando corrisponde una risposta; nell’esempio precedente tale può essere OK. Per dettagli maggiori circa i comandi si rimanda al capitolo successivo. 2.2.5 Transceiver RS485 Un transceiver, in italiano ricetrasmettitore, è un buffer driver bidirezionale che viene utilizzato per collegare, tipicamente, microprocessori. L’RS485 è uno standard EIA, del livello fisico (Modello OSI) di una connessione seriale a due fili, half-duplex6 e multi-punto. Secondo questo standard, il segnale viene gestito in maniera differenziale, ossia il bit in transito viene definito rispetto alla differenza di potenziale presente tra i due fili. Si noti che tale standard specifica esclusivamente le caratteristiche elettriche del trasmettitore e del ricevitore, ma non definisce alcun protocollo per la trasmissione dei dati. Per tale motivo è compito del programmatore adottare opportuni accorgimenti per verificare il corretto transito dei dati. Sebbene ciò sia un punto a sfavore, lo standard permette il raggiungimento di velocità di trasmissioni molto elevate: fino a 10 metri si raggiungono i 35Mbit/s; a 1200 metri si raggiungono i 100Kbit/s. In particolare, nel caso in esame, si fa riferimento al transceiver SN75HVD08, prodotto dalla Texas Instruments. Figura 2.10. Configurazione dei Pin - SN75HVD08, adottato da [10] Come si nota in figura, le linee A e B sono utilizzate per la comunicazione differenziale. Per il prosieguo dell’elaborato ci serve conoscere il DE (Driver Output Enable): se alto, il chip viene attivato come output (invio dati), viceversa quando è basso, il chip funziona da ricevente. Per completezza si cita la funzionalità dei pin rimanenti: sul pin R arrivano i dati letti dalla linea differenziali quindi va collegato alla linea RX della board; il pin D, invece, riceve i dati da inviare, quindi va collegato alla linea TX della board; RE, quando è basso, il chip è in grado di ricevere i dati dalla linea differenziale. Per quanto riguarda la programmazione, il transceiver va gestito semplicemente come una porta seriale. 2.2.6 Arduino UNO Come prima accennato, in fase di debugging si è utilizzata, in aggiunta, anche una board Arduino UNO. Si tratta di un microcontrollore basato sul chip Atmel ATmega328, ed è caratterizzata da 14 pin di I/O digitale, di cui 6 possono essere utilizzati con la tecnica del PWM, 6 input analogici, una porta USB e un jack di alimentazione. Così come l’Arduino Mega 2560 utilizza un chip ATmega16U2 come 5 Sono comandi consistenti in semplici stringhe. 6 La trasmissione è bidirezionale ma alternata 14 2 – La piattaforma Arduino Figura 2.11. Arduino UNO - Board convertitore USB-to-serial. Anche in questo caso sono presenti i pin per l’utilizzo del protocollo I2C, SDA e SCL. ATmega328 A testimonianza della sua minor potenza rispetto all’Arduino Mega 2560 è la presenza del chip ATmega328 caratterizzato dai seguenti fattori: una memoria flash da soli 32KB, una SRAM da 2KB, una EEPROM da 1 KB e una frequenza di clock di 16MHz. A ciò si aggiungono un insieme di 32 registri general-purpose direttamente collegati all’ALU, così come caratteristico dell’architettura AVR. Figura 2.12. Arduino UNO - Diagramma a blocchi 15 2 – La piattaforma Arduino In figura 2.12 è rappresentato il diagramma a blocchi del chip. 2.2.7 Display compatibile Hitachi HD44780 e Keypad numerico Il chip HD44780 è un cosiddetto “standard de facto”, dunque non è uno standard ufficialmente riconosciuto ma è largamente utilizzato tale per cui gode di elevata popolarità e ampia diffusione. A tal proposito Arduino mette a disposizione un set di API contenute nella libreria LiquidCrystal.h, permettendo l’astrazione dai dettagli interni del chip per la sua programmazione. In maniera analoga, per il keypad numerico 4x3 si utilizza una libreria analoga, Keypad.h che permette alla board di leggere il carattere premuto sul tastierino. 2.3 Prototipo di debugging Utilizzando i componenti descritti nel paragrafo 2.2, si è assemblato un prototipo al fine di effettuare il testing e il debugging del progetto definito nel capitolo successivo. Si presenta, a titolo illustrativo, una istantanea del suddetto. Figura 2.13. Snapshot del prototipo di testing e debugging 16 Capitolo 3 Descrizione e sviluppo del progetto 3.1 Panoramica Il progetto si pone il fine di andare a definire un sistema composito di tre tipi di nodi: • Centrale; • Concentratore; • Gateway; L’obiettivo è quello di realizzare una rete sensoriale, caratterizzata da concentratori, collegati tra di loro attraverso una rete wired o wireless che aderisca allo stack protocollare TCP/IP. In tale insieme di nodi, se ne contraddistinguono due fondamentali, il nodo centrale e il nodo gateway. Il nodo centrale ha il compito di definire ciclicamente le richieste di ottenimento dei dati sensoriali, mentre il gateway ha il compito di smistare le richieste provenienti dalla centrale verso i vari concentratori presenti sulla rete. In realtà, è possibile anche che un determinato concentratore sia dotato di un collegamento wired attraverso il protocollo EIA RS485 ad un bus su cui si interfacciano un certo numero di concentratori. Quindi, ogni concentratore può comportarsi come gateway verso la sottorete ad esso associata. Per tale motivo, il gateway, effettivamente, perde l’esclusività nel suo ruolo. È possibile, allora, adoperare la logica di smistamento richieste all’interno della centrale e lasciare al gateway la generica funzione di concentratore, aggiungendogli l’onere relativo al rilevamento delle richieste effettuate dalla centrale al fine di verificare eventuali malfunzionamenti. Se, infatti, il gateway non rileva più richieste gli si reca l’obbligo di instaurare una connessione dati (GSM/GPRS) con la centrale. In figura 3.1 è presente uno schema dell’architettura da realizzare. 3.1.1 Componenti del sistema Così come specificato, il sistema è composito di nodi, collegati tra di loro attraverso una rete TCP/IP. È opportuno richiedere, quindi che ciascun nodo sia caratterizzato dalla presenza di un modulo Ethernet e/o WiFi, oltre che da un modulo RS485. 17 3 – Descrizione e sviluppo del progetto Figura 3.1. Schema Architetturale realizzativo del sistema - adattato da [12] In più, il gateway e la centrale saranno dotati di un modulo GSM, per l’instaurazione di una connessione dati. Sotto tali requisiti, si considerano, a livello hardware, i nodi caratterizzati dalla seguente struttura: 1. Board Arduino Mega 2560, con chip ATmega2560 ; 2. Shield Arduino Ethernet Shield 2, con chip Wiznet W5500 ; 3. Modulo WiFi ESP8266; 4. Scheda con Transceiver RS485, con chip SN75HVD08 ; In aggiunta, così come specificato, sarà necessaria la presenza, nel gateway e nella centrale, di un componente: 5. Shield Arduino GSM Shield 2, con chip Quectec M10 ; 3.2 Soluzioni di sviluppo del sistema Per quanto riguarda lo sviluppo del sistema, l’idea adottata è stata quella di utilizzare un approccio di tipo bottom-up. In quest’ottica si aggiunge la necessità, data la complessità del sistema, di procedere per passi. In particolare, si osservi la natura modulare del sistema: ciò permette di dominare il livello di complessità progettuale, promuovendo uno sviluppo individuale dei singoli moduli. Innanzitutto notiamo, dal paragrafo 3.1, che il progetto include l’utilizzo di tre meccanismi di comunicazione: 1. Ethernet 802.3 o WiFi 802.11 (protocolli del livello fisico e MAC dello stack TCP/IP); 18 3 – Descrizione e sviluppo del progetto 2. RS485; 3. GSM/GPRS. Per tale motivo si è deciso di strutturare lo sviluppo del sistema incentrandolo, singolarmente, su ciascuno di questi tre protocolli. Sfruttando lo schema in figura 3.1, si va, dunque, a considerare come protocollo di comunicazione tra tutti i nodi, l’RS485. In tal senso, l’utilizzo del gateway come punto di smistamento delle richieste provenienti dalla centrale può risultare utile, in quanto ciò consente di incapsulare all’interno della centrale esclusivamente la logica di invio delle richieste. 3.2.1 Sviluppo con Protocollo RS485 - Architettura semplificata Così come accennato, in questa soluzione si fa riferimento ad un utilizzo esclusivo del protocollo RS485. In ottica semplificativa, inoltre, si preferisce, per il momento, escludere la possibilità di dotare i concentratori del collegamento verso una ulteriore sottorete. Ciò è riassunto nella figura 3.2. Figura 3.2. Schema Architetturale Semplificativo del sistema - adattato da [12] In questo caso quindi si considerano le seguenti assunzioni: • Il nodo centrale e il gateway sono legati tramite un collegamento fisico diretto sfruttante il protocollo RS485. Per tale motivo ambo i nodi dovranno essere in possesso di un transceiver RS485. • In riferimento al punto precedente, in questo esempio di comunicazione, la centrale si comporta come master mentre il gateway come slave. Quindi, il gateway attende di ricevere comandi dalla centrale per poter operare. 19 3 – Descrizione e sviluppo del progetto • Il gateway è collegato fisicamente alla rete di concentratori attraverso un bus condiviso, ove esso opera da master e i concentratori da slave. Per tale motivo, una volta ricevuto il comando, il gateway attiverà uno degli slave richiedendo l’acquisizione dei dati dai sensori. A tal fine si ritiene necessario che il gateway sia dotato aggiuntivamente di un collegamento RS485 verso il bus, allo scopo di poter comunicare con i concentratori. Indirizzamento Al fine di permettere al nodo centrale di richiedere dati sensoriali da un singolo oppure da un insieme di concentratori, si è definito, a livello applicazione, un tecnica di indirizzamento gerarchica. In virtù dello schema in figura 3.2, in questo ambito è sufficiente una gerarchia a due livelli: l’indirizzo, di 16-bit, viene suddiviso in due sotto-indirizzi di 8-bit. Definita come parte più significativa dell’indirizzo i primi 8-bit, tale identifica un gruppo di concentratori. Definita come parte meno significativa dell’indirizzo i rimanenti 8-bit, tale identifica il concentratore singolo nel gruppo. Ciò è schematizzato in figura 3.3. 0 0 | 1 0 1{z0 1 0 1} | 1 0 1{z0 1 0 1} Gruppo Concentratore nel gruppo Figura 3.3. Modalità di Indirizzamento adottata Inoltre, si definiscono le seguenti assunzioni: • un indirizzo la cui parte meno significativa è composita di tutti 1 mentre differisce nella parte più significativa, è equivalente ad un indirizzo di broadcast del gruppo. Ad esempio, l’in- dirizzo 1010101011111111 è un indirizzo di broadcast per il gruppo identificato dall’indirizzo 10101010xxxxxxxx. • un indirizzo interamente caratterizzato dalla presenza di bit alti, identifica un indirizzo di broadcast. In tal caso, la centrale richiederà ad ogni concentratore interfacciante il bus i dati sensoriali. Registrazione concentratori Così come definito precedentemente nel paragrafo 3.1, in riferimento alle assunzioni elencate in questa prima fase di sviluppo, sia il gateway che la centrale hanno necessità di conoscere l’eventuale presenza dei concentratori, e, in particolar modo, dei rispettivi propri indirizzi. Per tale motivazione è sorta l’esigenza di considerare la presenza di una struttura statica contenente l’insieme degli indirizzi dei concentratori attualmente presenti all’interno del sistema. La soluzione adottata è basata, in virtù della condivisione della stessa tra differenti devices, sul concetto di libreria. Nel dettaglio è stato definito un header file mappingStructure.h, contenente la struttura dati racchiudente l’insieme di indirizzi (applicazione e IPv4 secondo un mapping uno-a-uno), e un insieme di procedure, richiamabili su di essa la cui utilità risulterà più chiara nel prosieguo dell’elaborato. In figura 3.4 è rappresentato un estratto del file di intestazione della struttura. Come si nota, tale struttura è composita di tre campi: 20 3 – Descrizione e sviluppo del progetto 1 /* Definizione Struttura */ 2 3 typedef struct { 4 char *addresses[NUM_SENSORS]; 5 6 int ipAddresses[NUM_SENSORS][4]; 7 8 int size; 9 10 11 }MappingAddress; 12 13 /* Procedure di Utility */ 14 15 16 17 18 void initialize_addresses(MappingAddress*); void insert_row(MappingAddress*, char*, int , int , int , int ); int getIPfromAddress(MappingAddress* , char* , int* ); int getAddressFromIP(MappingAddress* , char* , int , int , int , int ); Figura 3.4. Estratto del file di intestazione: mappingStructure.h 1. char *addresses[NUM_SENSORS]: tale campo è un vettore di indirizzi, con dimensione pari al valore NUM_SENSORS. 2. int ipAddresses[NUM_SENSORS][4]: tale campo è un vettore di indirizzi IPv41 , con indicizzazione concorde al campo precedente. La memorizzazione dell’indirizzo è suddivisa per byte. Ciascun byte è rappresentato da un intero (anche se il tipo intero memorizza un valore di 2 bytes). 3. int size: è una variabile di conteggio, utilizzata per valutare il riempimento dei vettori staticamente definiti. Su di essa sono definite delle procedure di utility, in particolare quelle di interesse sono: • void initialize_addresses(MappingAddress*): è una procedura il cui obiettivo è l’inizializzazione della struttura. Deve, quindi, essere chiamata al momento della dichiarazione della stessa. Riceve come parametro di input un puntatore alla struttura MappingAddress precedentemente definita; • int getIPfromAddress(MappingAddress* , char* , int* ): è una procedura che ricevendo in ingresso l’indirizzo a livello applicativo restituisce il corrispondente indirizzo IP sotto forma di vettore di interi. Riceve come parametri di input un puntatore alla struttura MappingAddress e l’indirizzo a livello applicazione sotto forma di stringa, mentre come parametro di output richiede un vettore di interi di dimensione pari a 4. Se l’operazione va a buon fine, la procedura ritorna un valore intero 0, altrimenti -1. 1È necessario considerare questo campo in quanto il progetto finale prevede l’utilizzo di una rete TCP/IP 21 3 – Descrizione e sviluppo del progetto Struttura della Centrale Così come accennato nel paragrafo 2.1, lo sketch che viene caricato sulla piattaforma Arduino è caratterizzato da due procedure fondamentali, una di inizializzazione void setup(), e una di azione ciclica void loop(). In questo caso l’azione della centrale consiste, semplicemente, nel richiedere la lettura dei dati sensoriali rispetto ad un concentratore e attenderne la ricezione. Pertanto lo sketch si riduce alle poche righe di codice in figura 3.5. 1 /********** Setup ***********/ 2 3 void setup() { 4 Serial.begin(9600); Serial.println("Central"); Serial.println("Send an Address to Gateway"); 5 6 7 8 pinMode(SSerialTxControl, OUTPUT); 9 10 Serial2.begin(4800); 11 12 initialize_addresses(&sensorAddresses); 13 14 15 } 16 17 /********** Loop ***********/ 18 19 void loop() { 20 getSensorToCall(address); 21 22 sendRequest(address); 23 24 readValues(); 25 26 27 } Figura 3.5. Estratto dello sketch Centrale.ino relativo allo sviluppo con protocollo RS485 In questo estratto è possibile verificare come la funzione di setup() si limiti a inizializzare il data-rate delle interfacce Serial (per l’USB) e Serial2 (ai cui pin è collegata la scheda con il transceiver RS485) e la struttura statica contenente il mapping degli indirizzi applicazione-IP prima definita. Relativamente all’effettiva operazione effettuata dalla centrale, come già accennato, essa si riduce a tre operazioni fondamentali, richiamate ciclicamente: 1. getSensorToCall(): è una procedura che serve per ottenere l’indirizzo del concentratore da cui ottenere i dati; 2. sendRequest(): serve per inviare la richiesta di prelievo dati al concentratore tramite il gateway; 3. readValues(): attende l’arrivo dei dati dal gateway e li legge. 22 3 – Descrizione e sviluppo del progetto Struttura del Gateway Il gateway, come abbiamo visto, anche da un punto di vista schematico in figura 3.2, ha l’obiettivo di intercettare le richieste della centrale per smistarle correttamente ai concentratori presenti sul bus condiviso. Per tale motivo si richiede ad esso l’onere di gestire la comunicazione tra la centrale e i concentratori per il prelievo dei dati sensoriali. Anche in questo caso la funzione di setup() provvede all’inizializzazione del data-rate delle interfacce Serialx; in particolare, in questo caso viene inizializzato anche il Serial3, utilizzato per il collegamento con il bus RS485 del gateway (supponendo, dunque, un transceiver i cui pin TX e RX vengano collegati, rispettivamente, ai pin 14 e 15 della board). 1 /********** Setup ***********/ 2 3 void setup() { 4 Serial.begin(9600); Serial.println("Gateway"); Serial.println("[DEBUG] Write an Address, then press ENTER "); 5 6 7 8 pinMode(SSerialTxControl, OUTPUT); pinMode(CSerialTxControl, OUTPUT); 9 10 11 digitalWrite(SSerialTxControl, RS485Transmit); digitalWrite(CSerialTxControl, RS485Receive); 12 13 14 Serial2.begin(4800); Serial3.begin(4800); 15 16 17 initialize_addresses(&sensorAddresses); 18 19 20 } 21 22 /********** Loop ***********/ 23 24 void loop() { 25 getSensorFromSerial(address); //Per interazione con la Centrale: getSensorFromCentral(); 26 27 int type = getAddressType(address); 28 29 if (type == ISSINGLE) sendSingleRequest(address); 30 31 32 else if (type == ISGROUP) sendGroupRequest(address); 33 34 35 else sendBroadcastRequest(address); 36 37 38 /**** Per interazione con la Centrale **** sendEndToCentral(); **************************/ digitalWrite(CSerialTxControl, RS485Receive); 39 40 41 42 43 } Figura 3.6. Estratto dello sketch Gateway.ino relativo allo sviluppo con protocollo RS485 Relativamente alla funzione di loop() invece, in questo caso, essa consiste nell’attendere un comando 23 3 – Descrizione e sviluppo del progetto dalla centrale e inviare la richiesta ai concentratori interfaccianti il bus. In particolare: • getSensorFromCentral(): mette in attesa il gateway di un comando da parte della centrale. Ritorna quando viene rilevato un indirizzo valido. • getAddressType(): prelevato l’indirizzo, in virtù delle considerazioni effettuate precedentemente, è necessario valutare se l’indirizzo sia rivolto ad un singolo concentratore, ad un gruppo oppure all’intero set di concentratori. • send[Single/Group/Broadcast]Request(): racchiude la logica di invio delle richieste e ricezione dei dati dalla centrale. • sendEndToCentral(): inoltra i dati alla centrale. In figura 3.6 è rappresentato il codice di sviluppo del Gateway. Struttura del Concentratore (Sensore) Anche in questo caso il setup() inizializza i data-rate oltre che settare opportunamente il livello di tensione e il modo di utilizzo dei pin. Per ciò che riguarda, invece, la funzione di loop(), essa contiene la logica del concentratore. In particolare esso deve attendere di essere svegliato dal gateway e rispondere alle richieste. Come si nota nel codice in figura 3.7, tale logica è racchiusa nelle seguenti procedure: • WaitForRequest(): mette in attesa il concentratore di ricevere l’indirizzo applicativo. Se tale coincide con il proprio indirizzo, viene settata una variabile active ad un valore maggiore di zero. • AnswerRequest(): gestisce la logica di risposta ad una richiesta del gateway. Si noti che tale funzione viene richiamata esclusivamente se la variabile active corrisponde ad un valore maggiore di zero. Protocollo di comunicazione Centrale-Gateway Come abbiamo specificato nel paragrafo 2.2, il protocollo RS485 è uno standard del livello fisico relativo al modello ISO/OSI e per tale ragione non specifica alcuna regola circa lo scambio dei dati tra trasmettitore e ricevente. È, dunque, necessario stabilire a livello applicazione dei vincoli che devono rispettare la centrale e il gateway durante la loro comunicazione. In figura 3.8 sono presenti estratti di codice circa lo scambio dell’indirizzo tra le due entità in questione. Come si può notare, la centrale invia l’indirizzo utilizzando la porta seriale 2 (quindi il pin TX del transceiver è collegato al pin 16 della board, mentre l’RX al pin 17). D’altra parte, la centrale si mette in attesa (attiva) dell’indirizzo; non appena il numero di bytes presenti sulla porta seriale in lettura è pari alla dimensione dell’indirizzo, legge i bytes presenti sulla porta, li elimina dal buffer di input e li memorizza nel vettore di caratteri address, costruendo una stringa. Una volta ottenuto l’indirizzo, tramite la funzione getAddressType(), descritta precedentemente, il 24 3 – Descrizione e sviluppo del progetto 1 /***** Setup *****/ 2 3 void setup() { 4 Serial.begin(9600); Serial.println("Sensor "); 5 6 7 pinMode(SSerialTxControl, OUTPUT); 8 9 digitalWrite(SSerialTxControl, RS485Receive); 10 11 RS485Serial.begin(4800); 12 13 14 } 15 16 /***** Loop *****/ 17 18 void loop() { 19 WaitForRequest(); 20 21 if (active) { 22 23 AnswerRequest(); 24 25 } else { 26 27 Serial.println("Address doesn’t match! \nWaiting For Command"); waitForTransmit(); active = FALSE; 28 29 30 31 } digitalWrite(SSerialTxControl, RS485Receive); 32 33 } Figura 3.7. 1 Estratto dello sketch Sensore.ino relativo allo sviluppo con protocollo RS485 void sendRequest(char* address) { 1 2 2 Serial2.write(address); Serial2.flush(); 3 4 3 4 5 5 ... 6 6 7 8 7 } 8 Figura 3.8. void getSensorFromCentral(char* address) { ... if (Serial3.available() >= ADDRESS_SIZE) { for (int i = 0; i < ADDRESS_SIZE; i++) address[i] = (char)Serial3.read(); ... } Scambio indirizzo tra Centrale (a sinistra) e Gateway (a destra) gateway ne ottiene il tipo (singolo, broadcast o gruppo) e a seconda di esso richiama la funzione send[Single/Group/Broadcast]Request(). In figura 3.9 è rappresentato uno stralcio di codice, caratterizzante il caso in cui venga richiesto un indirizzo singolo di un concentratore. In particolare si noti che la centrale rimane in attesa mentre il gateway attende i dati dal/i concentratore/i. Non appena rileva un byte disponibile nel buffer di input, inizia a prelevare dati (ciò in corrispondenza con l’invocazione, da parte del gateway, di sendToCentral()). In particolare la centrale entra in un ciclo che termina quando riceve un carattere speciale, ossia "#". Tale simbolo è inviato dal gateway tramite la procedura sendEndToCentral(). 25 3 – Descrizione e sviluppo del progetto 1 2 3 void readValues() { ... while (true) { 1 2 3 4 4 if (Serial2.available()) { 5 6 5 6 7 7 c = (char)Serial2.read(); 8 8 9 9 if (c == ’#’){ ... return; } 10 11 12 13 10 11 12 13 14 14 ... 15 15 16 16 } 17 19 17 } 18 18 } 19 Figura 3.9. void loop() { ... sendSingleRequest(); ... sendEndToCentral(); ... } void sendSingleRequest(char address[]) { ... sendToCentral(sensors); ... } void sendToCentral(int* sensorValues){ ... for(int i = 0; i<NUM_ADC; i++){ Serial3.write(sensorValues[i]); Serial3.flush(); } } Scambio valori dei sensori tra Centrale (a sinistra) e Gateway (a destra) Protocollo di comunicazione Gateway-Concentratori Nella figura 3.10 sono presenti i passi fondamentali che descrivono la comunicazione tra il Gateway e i concentratori. In particolare si consideri anche il codice in figura 3.7. 1 2 3 4 5 6 7 8 void sendSingleRequest(char address[]) { sendWakeUPCmd(); Serial2.write(address); Serial2.flush(); ... int* sensors = receiveSensorValue(); ... } 1 2 3 4 5 6 7 8 9 10 11 12 13 9 void sendWakeUPCmd() { Serial2.write("WAKEU"); ... } 10 11 12 13 14 15 16 17 18 19 20 21 22 23 14 int* receiveSensorValue() { ... while (true) { if (Serial2.available() >= NUM_ADC) { ... for (int i = 0 ; i < NUM_ADC; i++) sensors[i] = Serial2.read(); ... } Figura 3.10. 15 16 17 18 19 20 21 22 23 void WaitForRequest(){ command = getCommand(); ... address = getAddress(); isMyAdd = compareString(address,ADDRESS,16); if (isMyAdd == 0) active = TRUE; } void AnswerRequest(){ ... RS485Serial.write(sensorReading); ... RS485Serial.write("#"); RS485Serial.flush(); ... active = FALSE; } void waitForTransmit() { ... if (RS485Serial.available()){ if ((char)RS485Serial.read() == ’#’) return; ...} Comunicazione tra Gateway (a sinistra) e Concentratore (a destra) Si osservi che il concentratore è in attesa di essere risvegliato tramite la procedura getCommand(); allo stesso tempo il gateway avrà utilizzato la funzione sendWakeUpCmd() per inviare il comando e successivamente l’indirizzo. Dopo aver recepito l’indirizzo, il concentratore verifica se coincide con il proprio e, dunque, setta opportunamente la variabile active. Nel caso in cui recepisca un indirizzo non 26 3 – Descrizione e sviluppo del progetto conforme con il proprio si mette in attesa richiamando la funzione WaitForTransmit(), che attende la fine della trasmissione in corso. Nel caso contrario, ottenuti i valori dai sensori, il concentratore li invierà al gateway effettuando una scrittura sulla porta seriale. Quest’ultimo stanzierà in attesa attiva fintanto che non riceva i valori, attraverso la procedura receiveSensorValue(). Esempio di Testing Sfruttando il prototipo a disposizione, descritto in figura 2.13, si va ad effettuare un test circa la comunicazione tra il Gateway e il Concentratore. In questo caso rappresentiamo il Gateway attraverso la board Arduino Mega 2560 mentre il Concentratore attraverso la board Arduino UNO. Entrambi sono dotati di una scheda con transceiver RS485 mediante la quale comunicano. (a) Monitor Seriale Gateway Figura 3.11. (b) Monitor Seriale Concentratore Esempio di testing della piattaforma In figura 3.11 è illustrato un esempio di comunicazione tra tali componenti, utilizzando per il debug l’interfaccia Serial e dunque il monitor seriale. 3.2.2 Sviluppo con Protocollo RS485 - Architettura raffinata Così come chiarito nel paragrafo 3.1, il progetto finale prevede una complessità maggiore rispetto allo sviluppo appena effettuato nel paragrafo 3.2.1, consistente in un’estensione gerarchica dei concentratori. Continuando lo sviluppo attraverso il protocollo wired RS485, osserviamo la necessità di modifica di alcuni meccanismi affinché il progetto rispecchi l’architettura desiderata, raffigurata in figura 3.12. Riconsiderando le assunzioni precedentemente fatte, notiamo che tali non possano considerarsi più vere a causa di differenti aspetti. In questo caso, infatti, bisogna tener traccia delle seguenti problematiche: 1. L’indirizzamento gerarchico a due livelli, definito nell’architettura precedentemente sviluppata, non può considerarsi più valido perché impossibilita l’opportunità di sollecitare un concentratore non direttamente collegato al bus principale; 2. I singoli concentratori sono essi stessi dei gateway verso il bus più esterno. 27 3 – Descrizione e sviluppo del progetto Figura 3.12. Schema Architetturale Raffinato del sistema - adattato da [12] Ciò implica modifiche nel sistema di indirizzamento e nella logica di smistamento dei messaggi dei gateway/concentratori. Indirizzamento La soluzione adottata per risolvere il problema dell’indirizzamento nell’architettura in figura 3.12 è risolto adottando un indirizzo suddiviso in tre livelli. Supponendo di avere a disposizione un indirizzo di 16bit, l’idea richiede di suddividerlo in tre parti, ottenendo, dunque, due sotto-indirizzi da 5-bit e uno da 6-bit. Ciò permette di indirizzare fino a 32 gruppi, ciascuno contenente fino a 32 concentratori e ogni concentratore può essere collegato ad un bus secondario con al più 64 concentratori aggregati. In tale ottica la parte più significativa dell’indirizzo individua il gruppo di concentratori, la parte di intermezzo individua il concentratore nel gruppo ed infine, la parte meno significativa individua il concentratore aggregato al bus secondario rispetto al singolo individuato nella parte centrale. 0 | 1 0 1 {z Gruppo 0} 1 0 1 0 1} 0 1 0 {z 1 0 1} | {z | Concentratore nel gruppo Concentratore II livello Figura 3.13. Modalità di Indirizzamento a tre livelli In virtù di una tale ripartizione, si definiscono le seguenti assunzioni: • un indirizzo la cui parte meno significativa corrisponde ad un insieme di 1 e la rimanente parte risulta composita di bit non necessariamente unitari è un indirizzo di broadcast per il sottogruppo, ossia per l’insieme dei concentratori collegati al bus secondario rispetto ad un concentratore legato al bus principale. Ad esempio, l’indirizzo 1010101010111111 sollecita tutti i concentratori che sono collegati al concentratore individuato nel gruppo 10101 dal sotto-indirizzo 01010. 28 3 – Descrizione e sviluppo del progetto • un indirizzo le cui parti di intermezzo e meno significativa sono costituite da tutti 1 è un indirizzo di broadcast per il gruppo individuato dalla parte di indirizzo più significativa. • un indirizzo costituito interamente da bit alti è un indirizzo di broadcast per l’intero insieme di concentratori. • un indirizzo la cui parte meno significativa è costituita da tutti bit 0 localizza il gateway individuato dall’indirizzo corrispondente alla parte di intermezzo nel gruppo individuato dalla parte più significativa. In questo caso la struttura memorizzante gli indirizzi dei concentratori rimane invariata, in quanto si modifica solo la modalità di rappresentazione degli stessi. Analogamente, non si modifica la logica della centrale che, dunque, si limita a trasmettere comandi al gateway e ad attendere risposta come nel codice in figura 3.5. Riscontriamo cambiamenti, invece, sia nella logica del concentratore e sia nella logica del gateway, in conseguenza al problema dell’indirizzamento. Struttura del Concentratore Come già accennato, nell’architettura a tre livelli presentata in figura 3.12, il ruolo del concentratore si amplia in virtù della presenza di un bus secondario a cui vengono collegati ulteriori concentratori. In tale ottica esso può essere rappresentato alla stessa stregua di un gateway di secondo livello, incapsulando sia la logica di ottenimento dell’indirizzo dal gateway di primo livello, sia la logica di smistamento delle richieste ai concentratori secondari. Così come definito in figura 3.14, notiamo che differentemente dalla funzione di inizializzazione valutata nel precedente progetto, in questo caso vengano utilizzate due porte seriali. In particolare, la porta Serial2 è utilizzata per la comunicazione del concentratore con il gateway, coerentemente con il progetto precedente, mentre la porta Serial3 è utilizzata per la comunicazione con il set di concentratori legati al bus secondario. Per quanto riguarda la funzione di loop(), tale si limita ad attendere l’arrivo di una richiesta dal gateway e a rispondere ad essa. Nel dettaglio: • WaitForRequest(): mette il concentratore in attesa di ricevere il comando di Wake-Up e poter, dunque, ricevere l’indirizzo; • AnswerRequest(): verifica se l’indirizzo è relativo al concentratore d’interesse e in caso affermativo risponde alla richiesta. Struttura del Concentratore (Sensore), del Gateway e della Centrale In questa architettura, è bene sottolineare che il ruolo del concentratore di secondo livello è analogo al ruolo del concentratore nello sviluppo considerato nel paragrafo 3.2.1. Ciò si ripete anche per quanto riguarda la struttura del gateway e della centrale, che ad eccezione di lievi modifiche riguardanti la logica, rimangono pressoché invariate. 29 3 – Descrizione e sviluppo del progetto /***** SETUP *****/ 1 2 3 void setup() { 4 Serial.begin(9600); Serial.println("Concentratore "); 5 6 7 8 pinMode(SSerialTxControl, OUTPUT); pinMode(CSerialTxControl, OUTPUT); 9 10 11 digitalWrite(SSerialTxControl, RS485Receive); digitalWrite(CSerialTxControl, RS485Transmit); 12 13 14 delay(10); 15 16 initialize_addresses(&m); 17 18 Serial2.begin(4800); Serial3.begin(4800); 19 20 21 } 22 23 /**** LOOP *****/ 24 25 void loop() { 26 WaitForRequest(); 27 28 AnswerRequest(); 29 30 31 } Figura 3.14. Estratto dello sketch Concentratore.ino relativo allo sviluppo con protocollo RS485 Raffinato Protocollo di comunicazione Gateway-Concentratore Per ciò che riguarda il protocollo di comunicazione utilizzato tra Gateway e Concentratore (di primo livello) è utile osservare che tale rimane invariato ad eccezione di due aspetti, rispettivamente relativi alla gestione dell’indirizzo e all’invio/ricezione dei dati. In particolare, considerando lo spezzone di codice in figura 3.15, si noti che il numero di dati che può ricevere il gateway non è deterministico e dipende dal numero di concentratori collegati al concentratore primario. Per tale motivo si utilizza una variabile size al fine di memorizzare la dimensione del vettore di valori. D’altra parte, considerando il concentratore di primo livello, esso deve verificare se l’indirizzo sollecitato dalla centrale è di un membro collegato al bus secondario, oppure se è riferito a sé stesso, oppure se è un indirizzo broadcast. Si sottolinea che, per convenzione, un indirizzo avente gli ultimi 6-bit bassi individua il concentratore di primo livello individuato nel gruppo dallo specifico indirizzo. 3.2.3 Sviluppo con Protocollo TCP/IP - Ethernet Seguendo l’approccio bottom-up, si è partiti da una struttura rispecchiante un’architettura semplificata del sistema da progettare e si è visto che con qualche opportuna modifica si è potuto ottenere la struttura complessiva desiderata. In realtà, osservando lo schema in figura 3.1, l’interazione tra la centrale e il 30 3 – Descrizione e sviluppo del progetto 1 int* receiveSensorValue(int* size) { 1 2 2 int sensors[NUM_ADC*64+1]; *size = 0; while (1) { 3 4 5 3 4 5 6 6 if (Serial2.available() >= NUM_ADC) { Serial.println("Getting Sensors... "); while(Serial2.peek()!=’#’){ sensors[*size] = Serial2.read(); (*size)++; } 7 8 9 10 11 12 7 8 9 10 11 12 13 13 Serial.println("Sensors Obtained!"); break; 14 15 14 15 } 16 16 17 17 } 18 18 19 19 return sensors; 20 20 21 22 21 } 22 Figura 3.15. int getAddressType(char* address) { int isGrpBrdcst = 1; int isSingleGrp = 1; int isMe = 1; for (int i = 10; i < 16; i++) { if (address[i] != ’1’) { isGrpBrdcst = 0; break; } } for (int i = 10; i < 16; i++) { if (address[i] != ’0’) { isMe = 0; break; } } if (isGrpBrdcst == 0 && isMe == 0) return ISME; if (isGrpBrdcst == 1) return ISGRPBROADCAST; return ISGRPSINGLE; } Comunicazione tra Gateway (a sinistra) e Concentratore (di primo livello) (a destra) gateway e tra il gateway e i concentratori dovrebbe essere effettuata attraverso lo standard TCP/IP. In questa prima parte ci si concentra sul protocollo wired Ethernet 802.3 utilizzando, nell’architettura prototipale, un Arduino Shield Ethernet 2. Nell’ottica di progetto adoperata, i cambiamenti rispetto agli sviluppi presenti nei paragrafi 3.2.1 e 3.2.2 sono pochi e non modificanti la logica implementata, in quanto relativi al differente protocollo adoperato. Si noti, d’altro canto, che, in riferimento alla figura 3.1, è necessario che il gateway e la centrale siano dotati di connettività GSM/GPRS al fine di evidenziare malfunzionamenti (attraverso un meccanismo di poling), così come specificato nella sezione 3.1. Struttura della Centrale In virtù di quanto specificato, notiamo che la funzione di setup() si arricchisce con l’insieme dei comandi necessari per l’inizializzazione del modulo Ethernet e GSM, così come riscontrabile in figura 3.16. A differenza dello sviluppo con RS485, inoltre, vengono a mancare le operazioni di inizializzazione delle interfacce seriali, non più utilizzate (eccetto l’USB). Notiamo, inoltre, che in questa architettura, così come preannunciato, il gateway perde le sue funzionalità. In tal senso la logica di smistamento delle richieste è immersa nella centrale. È possibile verificare che la centrale, infatti, effettua ciclicamente tre azioni, consistenti nelle procedure: 1. lookForGsmRequest(): verifica l’eventuale presenza di una richiesta di connessione da parte del gateway; 2. getSensorToCall(): mira ad ottenere l’indirizzo applicativo del concentratore da sollecitare; 3. AnswerRequest(): verifica la natura dell’indirizzo, invia la richiesta e pone la centrale in attesa di ricevere risposta. 31 3 – Descrizione e sviluppo del progetto 1 /**** SETUP *****/ 2 3 4 5 6 7 8 void setup() { Ethernet.begin(mac, ip); delay(1000); Serial.begin(9600); Serial.println("Gateway"); Serial.println("[DEBUG] Write an Address, then press ENTER "); 9 initialize_addresses(&sensorAddresses); 10 11 boolean notConnected = true; 12 13 while (notConnected) { if ((gsmAccess.begin(PINNUMBER) == GSM_READY) & (gprs.attachGPRS(GPRS_APN, GPRS_LOGIN, GPRS_PASSWORD) == GPRS_READY)) { notConnected = false; } else { Serial.println("Not connected"); delay(1000); } } 14 15 16 17 18 19 20 21 22 23 24 } 25 26 /**** LOOP *****/ 27 28 29 30 31 32 void loop() { lookForGsmRequest(); getSensorToCall(); AnswerRequest(); } Figura 3.16. Estratto dello sketch Centrale.ino relativo allo sviluppo con protocollo Ethernet Struttura del Gateway Per quanto riguarda il gateway, così come affermato precedentemente, esso perde alcune delle sue funzionalità a scapito di altre. In particolare esso si comporta come un semplice concentratore, con l’aggiunta che viene utilizzato anche per verificare la corretta funzione del sistema, attraverso il meccanismo di polling. In particolare oltre alle classiche operazioni di inizializzazione, la funzione setup() memorizza il valore in millisecondi del tempo trascorso dall’accensione del sistema attraverso la primitiva millis(). Tale primitiva viene utilizzata per verificare l’inattività della centrale. In alternativa, opportunamente commentata, è utilizzata una libreria esterna, TimerThree.h, da [18], che sfrutta il meccanismo delle interruzioni del Timer. D’altra parte la funzione di loop() si limita a verificare eventuali richieste (anche polling) e fornirne risposta. In più, nel caso in cui occorra l’inattività della centrale, stabilisce una connessione GPRS con essa. In particolare, tutto ciò è realizzato attraverso le procedure: 1. AnswerRequest(): non bloccante, risponde ad una eventuale richiesta (o polling) della centrale; 2. createGPRSConnection(): stabilisce una connessione GPRS con la centrale. 32 3 – Descrizione e sviluppo del progetto 1 2 3 4 5 6 /**** SETUP ****/ void setup() { Serial.begin(9600); Ethernet.begin(mac, ip); delay(1000); Serial.println("Gateway "); 7 8 9 10 11 12 13 14 15 16 17 18 19 digitalWrite(CSerialTxControl, RS485Transmit); delay(10); initialize_addresses(&m); /* Opzione Timer3 * Timer3.initialize(500000); Timer3.attachInterrupt(createGPRSConnection); Timer3.start(); */ Serial3.begin(4800); server.begin(); start = millis(); } 20 21 /*** LOOP ***/ 22 23 24 25 26 27 28 29 30 void loop() { client = server.available(); AnswerRequest(); checkpoint = millis(); if(checkpoint - start >= MAX_TIME) createGPRSConnection(); } } Figura 3.17. Estratto dello sketch Gateway.ino relativo allo sviluppo con protocollo Ethernet Struttura del Concentratore Il concentratore si discosta, in termini implementativi, di poco dallo sviluppo effettuato attraverso il protocollo RS485. In sostanza vengono a modificarsi solo le porzioni di codice relative all’interazione con la centrale (che fa le veci del gateway per quanto specificato precedentemente). Il protocollo utilizzato per l’interazione con i concentratori di secondo livello rimane l’RS485, quindi le porzioni di codice corrispondenti rimangono intatte. Notiamo che la funzione di setup() rimane pressoché identica rispetto all’implementazione del gateway, eccezion fatta per l’inizializzazione dei parametri relativi al polling. La funzione di loop(), d’altra parte, risulta effettuare le stesse operazioni effettuate dal gateway, ad eccezione del richiamo della procedura di creazione della connessione GPRS con la centrale. Infine, è bene sottolineare che, in accordo alla figura 3.1, rappresentante l’architettura complessiva e definitiva del sistema, la struttura del sensore rimanga invariata rispetto a quella presentata nel paragrafo 3.2.2. Protocollo di comunicazione Centrale-Gateway In figura 3.19 sono riportati dei frammenti rispetto alle procedure che definiscono la comunicazione tra cemtrale e gateway. 33 3 – Descrizione e sviluppo del progetto 1 /***** SETUP ******/ 2 3 4 5 void setup() { Serial.begin(9600); Ethernet.begin(mac, ip); 6 delay (1000); 7 8 Serial.println("Concentratore "); 9 10 pinMode(CSerialTxControl, OUTPUT); digitalWrite(CSerialTxControl, RS485Transmit); 11 12 13 delay(10); 14 15 initialize_addresses(&m); 16 17 Serial3.begin(4800); server.begin(); 18 19 20 21 } 22 23 24 25 26 27 28 29 /***** LOOP ******/ void loop() { client = server.available(); if (client) { AnswerRequest(); } } Figura 3.18. Estratto dello sketch Concentratore.ino relativo allo sviluppo con protocollo Ethernet Innanzitutto notiamo che la la funzione lookForGsmRequest() verifica l’eventuale presenza di richieste di connessione da parte del gateway pervenute in conseguenza all’assenza del polling. In tale ottica si constata che il gateway poiché è anche un concentratore, e dunque, anch’esso può ricevere richieste di prelievo dati dai sensori, è necessario separare le sollecitazioni di polling da quelle di richieste. Per tale motivo, quando la centrale effettua il polling, invia un carattere speciale al gateway, ossia "*"; lato ricevente, ad ogni sollecitazione, il gateway verifica, attraverso la procedura getAddress() la natura della sollecitazione, definendo l’azione da intraprendere. A conclusione notiamo, dunque, che grazie all’approccio di progettazione adoperato, le modifiche da effettuare allo sviluppo presentato nella sezione 3.2.2 sono minimali, riguardanti per lo più le primitive di gestione della comunicazione. Protocollo di comunicazione Centrale-Concentratore In questo caso si ripetono esattamente tutte le considerazioni effettuate nella descrizione del protocollo di comunicazione tra la centrale e il gateway, escludendo, però, quelle relative all’insieme di operazioni necessarie all’effettuazione del polling e alla conseguente instaurazione di una connessione GPRS. Esempio di Testing Così come descritto nel paragrafo 3.2.1, anche in questo caso si riporta un esempio di messa in esercizio del sistema, simulata attraverso la circuiteria a disposizione. In particolare l’esempio di testing prevede 34 3 – Descrizione e sviluppo del progetto 1 void lookForGsmRequest() { 1 2 GSMClient client = server.available(); if (client) { while (client.connected()) { if (client.available()) { //do something ... 3 4 5 6 7 8 9 3 4 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 29 30 31 32 33 34 7 8 } 9 10 void sendRequest(char* address) { ... if(compareString(address,GATEWAY_ADD,16)!=0) pollingToGateway(); ... if(client.connect(server, PORT)) { client.write(address); client.flush(); int size; sensors = getDataSensor(&size); printResult(sensors, size); while(client.connected()); client.stop(); } } } 11 12 13 14 15 16 17 18 20 start = millis(); } 21 22 23 } 24 25 26 27 void pollingToGateway() { IPAddress server(192, 168, 1, 100); if (client.connect(server, PORT)) { client.write("*"); while(client.connected()); client.stop(); 28 29 30 31 void sendRequest(char address[]) { ... int* sensors = receiveSensorValue(); ... waitForEnd(); ... Serial.println("Send to Central"); 32 33 34 35 } 36 } 19 35 37 int type = getAddressType(address); if (type == ISME) { readSensor(); sendEnd(); } else if (type == ISGRPBROADCAST) { ... } else { sendRequest(address); sendEnd(); } 6 27 28 if (client) { if (getAddress() == 0) { 5 10 11 void AnswerRequest(){ 2 for (int i = 0; i < NUM_ADC; i++) server.write(sensors[i]); ... 36 } 37 Figura 3.19. } Comunicazione tra Centrale (a sinistra) e Gateway (a destra) con Ethernet l’utilizzo della piattaforma prototipale come un concentratore e l’utilizzo di un comune browser come centrale. Poiché nello sviluppo appena concluso non si è utilizzato alcun protocollo HTTP, al fine di permettere la comunicazione con il browser è necessario, all’interno dell’implementazione della centrale, specificarlo. Ciò si riduce all’aggiunta di stringhe costituenti l’intestazione HTTP. In via semplificativa si assume che le richieste effettuate dal browser riguardino esclusivamente il concentratore a disposizione. Sotto tali assunzioni, la funzione readMySensor() si modifica come illustrato in figura 3.20. In questo modo, attraverso i comandi specificati nell’intestazione HTTP, il contenuto nella pagina del browser verrà formattato come html. Per effettuare un test di messa in esercizio del sistema occorre semplicemente collegare la porta Ethernet della scheda Arduino Ethernet Shield 2 ad uno switch Ethernet o ad un router che possiede tale caratteristica attraverso un cavo RJ45. A questo punto, una volta caricato lo sketch sulla piattaforma, collegandosi attraverso un browser all’indirizzo IPv4 del concentratore è possibile ottenere l’output così come specificato in figura 3.21. 35 3 – Descrizione e sviluppo del progetto 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 void readMySensor() { client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println("Connection: close"); client.println(); client.println("<!DOCTYPE HTML>"); client.println("<html>"); client.println("Lettura Sensore: <br> <br>"); for (int analogChannel = 0; analogChannel < 6; analogChannel++) { int sensorReading = analogRead(analogChannel); client.print(sensorReading); client.print("<br>"); } Serial.println("Value Sent"); } Figura 3.20. Estratto dello sketch Concentratore.ino relativo alla funzione ReadMySensor() - Ethernet (a) Browser Web - 192.168.1.101 Figura 3.21. 3.2.4 (b) Monitor Seriale Concentratore Esempio di testing della piattaforma con Ethernet Sviluppo con Protocollo TCP/IP - WiFi In questa sezione si va a presentare una prototipazione del progetto esposto in figura 3.1, attraverso lo standard IEEE WiFi 802.11. Si noti che poiché viene a modificarsi esclusivamente il mezzo di comunicazione, le uniche modifiche da effettuare, rispetto al progetto di sviluppo attraverso lo standard Ethernet, implementato nel paragrafo 3.2.3, sono relative alle primitive necessarie all’utilizzo del mezzo. Nel dettaglio si ricordi, dalla sezione 2.2, che il modulo utilizzato nel prototipo per usufruire della connessione WiFi è un ESP8266, il cui funzionamento è legato all’impiego di comandi AT. Struttura della Centrale In figura 3.22 è rappresentato un’estratto della struttura dello sketch relativo all’implementazione della centrale. In particolare si noti come il flusso logico delle operazioni effettuate da essa rimanga invariato rispetto allo sviluppo presentato con lo standard Ethernet e che le differenze sono visibili nella funzione di inizializzazione. Come possiamo osservare, nella funzione di setup() sono evidenziati l’insieme dei comandi necessari alla messa in esercizio del modulo WiFi. In particolare, tali comandi sono invocati 36 3 – Descrizione e sviluppo del progetto 1 /**** SETUP *****/ 2 3 4 5 6 7 8 9 void setup() { ... String cwjap="AT+CWJAP=\""; cwjap+=SSID; cwjap+="\",\""; cwjap+=PASS; cwjap+="\"\r\n"; 10 String cipsta="AT+CIPSTA_CUR=\""; cipsta+=myIp; cipsta+="\",\"192.168.1.1\",\"255.255.255.0\""; cipsta+="\"\r\n"; 11 12 13 14 15 sendData("AT+RST\r\n",2000); sendData("AT+CWMODE=1\r\n",1000); sendData(cwjap,2000); sendData(cipsta,5000); sendData("AT+CIFSR\r\n",5000); //Per Debug sendData("AT+CIPMUX=1\r\n",1000); 16 17 18 19 20 21 22 } 23 24 /**** LOOP *****/ 25 26 27 void loop() { lookForGsmRequest(); 28 getSensorToCall(); 29 30 AnswerRequest(); 31 32 } Figura 3.22. Estratto dello sketch Centrale.ino relativo allo sviluppo con WiFi attraverso l’utilizzo della routine sendData(), di cui si discuterà a seguire. Le operazioni effettuate in fase di inizializzazione prevedono: 1. Restart del modulo attraverso il comando AT+RST; 2. Setting della modalità di funzionamento stazione ottenuto attraverso il comando AT+CWMODE=1; 3. Join all’Access Point attraverso il comando AT+CWJAP="SSID","PASS"; 4. Setting dell’IP Statico attraverso il comando AT+CIPSTA_CUR = "IP", "GATEWAY", "SUBNET-MASK"; 5. Setting della connessione multipla attraverso il comando AT+CIPMUX = 1. Per quanto riguarda, invece, la struttura della procedura loop(), essa rimane inalterata. Vengono a modificarsi esclusivamente le istruzioni necessarie alla comunicazione con i concentratori ma non la logica della stessa. Ricordando quanto esposto nella sezione 3.2.3, la funzione AnswerRequest() ha il compito di inviare la richiesta al/i concentratore/i e attendere la ricezione dei dati. In figura 3.23 sono presenti degli estratti dello sketch Centrale.ino che illustrano l’invio della richiesta di prelievo dati e del polling al gateway. In particolare si noti che la centrale, affinché sia in grado di comunicare con i concentratori deve stabilire con essi una connessione TCP, e, di conseguenza, ad essa devono essere noti 37 3 – Descrizione e sviluppo del progetto sia l’indirizzo IPv4 sia il porto in cui il server è in ascolto. La comunicazione, ovviamente, avviene solo se è possibile instaurare la connessione. Ciò viene eseguito attraverso il comando AT+CIPSTART. Una volta terminate le operazioni, viene richiesta la chiusura della connessione attraverso il comando AT+CIPCLOSE. 1 2 3 4 5 6 7 8 9 10 void SendRequest(){ ... String server = String(ip[0]); ... String cipStart = "AT+CIPSTART="; cipStart += connectionId; cipStart += ",TCP,"; cipStart += server; cipStart += ","; cipStart+= PORT; 1 String gateway = "192.168.1.100"; 3 4 String cipStart = "AT+CIPSTART="; cipStart += connectionId; cipStart += ",TCP,"; cipStart += gateway; cipStart += ","; cipStart += PORT; 5 6 7 8 9 10 11 11 String res = sendData(cipStart, 2000); 12 String res = sendData(cipStart, 2000); 12 13 13 if(res.compareTo("OK") == 0) { 14 if (res.compareTo("OK") == 0) { String charForPolling = "*"; String cipSend = "AT+CIPSEND="; cipSend += connectionId; cipSend += ","; cipSend += charForPolling.length(); cipSend += "\r\n"; sendData(cipSend, 1000); sendData(charForPolling, 1000); 14 15 15 String cipSend = "AT+CIPSEND="; cipSend += connectionId; cipSend += ","; cipSend +=server.length(); cipSend +="\r\n"; sendData(cipSend,1000); sendData(address,1000); int size; sensors = getDataSensor(&size); printResult(sensors, size); String closeCommand = "AT+CIPCLOSE="; closeCommand+=connectionId; closeCommand+="\r\n"; sendData(closeCommand,3000); } 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 16 17 18 19 20 21 22 23 24 sendData("AT+CIPCLOSE", 1000); String closeCommand = "AT+CIPCLOSE="; closeCommand += connectionId; closeCommand += "\r\n"; sendData(closeCommand, 3000); 25 26 27 28 29 30 } 31 32 void pollingToGateway() { 2 } 31 } 32 } Figura 3.23. Estratto dello sketch Centrale.ino relativamente alle funzioni SendRequest() (a sinistra) e pollingToGateway() (a destra) Struttura del Gateway e Concentratore In riferimento alla figura 3.1 si osserva come, utilizzando una rete TCP/IP, il gateway e il concentratore si comportano, rispetto alla centrale, alla stregua dei server. È pertanto necessario definire, attraverso comandi AT, rispetto alla implementazione della centrale, oltre all’indirizzo IP anche il porto di listening. Ciò avviene attraverso il comando seguente che registra, nel caso in esempio, il server sul porto 8080: sendData("AT+CIPSERVER=1,8080\r\n",1000); È importante osservare che, così come nello sviluppo tramite Ethernet, anche in questo caso la struttura del concentratore (sensore) rimane invariata in quanto esso continua a comunicare con il concentratore che abbiamo definito di primo livello attraverso un collegamento a bus RS485. 38 3 – Descrizione e sviluppo del progetto Procedura di invio comandi e dati Così come si è accennato poc’anzi, l’invio del comandi AT viene effettuato attraverso l’utilizzo della procedura String sendData(String cmd, int timeout) che utilizza come parametri di input: • String cmd che rappresenta il comando AT; • int timeout che rappresenta il tempo massimo di attesa di una risposta; Tale procedure ritorna la risposta, ossia l’esito del comando. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 String sendData(String command, const int timeout) { String response = ""; Serial1.print(command); // send the read character to the esp8266 long int time = millis(); while( (time+timeout) > millis()) { while(Serial1.available()) { char c = Serial1.read(); response+=c; } } return response; } Figura 3.24. Implementazione della procedura sendData() In figura 3.24 è rappresentata l’implementazione della procedura di invio comandi. Per quanto riguarda l’invio di dati è importante osservare che, in riferimento all’utilizzo del modulo ESP8266, prima dell’invio effettivo degli stessi è necessario, tramite il comando AT+CIPSEND, definire la lunghezza della stringa che s’intende trasmettere e fornire l’id della connessione. Si consideri l’esempio, in figura 3.25, dove è possibile notare l’invio del comando AT+CIPSEND specificante il numero di bytes da trasmettere e successivamente il carattere trasmesso. 1 void sendEnd() { 2 String endChar = "#"; String cipSend = "AT+CIPSEND="; cipSend += connectionId; cipSend += ","; cipSend += endChar.length(); cipSend += "\r\n"; 3 4 5 6 7 8 9 sendData(cipSend, 1000); sendData(endChar, 1000); 10 11 12 String closeCommand = "AT+CIPCLOSE="; ... 13 14 15 } Figura 3.25. Modalità di invio dati, estratto dallo sketch Concentratore.ino 39 3 – Descrizione e sviluppo del progetto Esempio di Testing Analogamente a quanto definito nei paragrafi 3.2.1 e 3.2.3, si riporta un esempio di messa in opera del sistema. Anche in questo caso si simula una versione semplificata del sistema, eseguibile sulla piattaforma a disposizione, che agirà da concentratore. Analogamente all’esempio di testing tramite Ethernet, il client sarà rappresentato da un browser web e per tale motivo, al fine di permettere la comunicazione, è necessario aggiungere le stringhe relative all’intestazione HTTP. Sotto tali assunzioni, la funzione readMySensor() si modifica come illustrato in figura 3.26 con l’aggiunta della procedura sendHTTPHeader(). 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 void sendHTTPHeader(int connectionId){ String http[6]; String cipSend = ""; http[0] = "HTTP/1.1 200 OK\r\n"; http[1] = "Content-Type: text/html\r\n\r\n"; http[2] = "<!DOCTYPE html>\r\n"; http[3] = "<html>\r\n"; http[4] = "<body>\r\n"; http[5] = "Lettura Sensori: <br><br>"; for(int i = 0 ; i < 6; i++) { cipSend = "AT+CIPSEND="; cipSend += connectionId; cipSend += ","; cipSend +=http[i].length(); cipSend +="\r\n"; sendData(cipSend,1000); 18 sendData(http[i],1000); } 19 20 } 21 22 23 void readMySensor() { 24 connectionId = Serial1.read()-48; delay(1); sendHTTPHeader(connectionId); 25 26 27 28 String breakLine = "<br>"; for (int analogChannel = 0; analogChannel < 6; analogChannel++) { 29 30 31 String sensorReading = (String)analogRead(analogChannel); ... 32 33 34 } String closeCommand = "AT+CIPCLOSE="; ... Serial.println("Value Sent"); 35 36 37 38 } Figura 3.26. Estratto dello sketch Concentratore.ino relativo alla funzione readMySensor() - WiFi L’intestazione HTTP così descritta permetterà la visualizzazione dell’output all’interno del browser attraverso una formattazione html. Per effettuare un test di messa in esercizio del sistema occorre semplicemente disporre di un access point oppure di un router che integra tale funzione. A questo punto, una volta caricato lo sketch sulla 40 3 – Descrizione e sviluppo del progetto piattaforma, collegandosi attraverso un browser all’indirizzo IPv4 del concentratore è possibile ottenere l’output così come specificato in figura 3.27. (a) Browser Web - 192.168.1.101 Figura 3.27. 3.3 (b) Monitor Seriale Concentratore Esempio di testing della piattaforma con WiFi Scheduling delle Richieste Al fine di prevedere un meccanismo di invio periodico delle richieste ai concentratori, è stato realizzato, con l’intento di governare la logica della centrale, un semplice scheduler, principalmente basato sull’algoritmo di scheduling per task periodici EDF2 , non preemptive, con supporto anche per task aperiodici. Poiché non supporta la prelazione dei task, l’algoritmo non può essere considerato ottimo nel senso dello scheduling [5]. Esso è implementato come una libreria Arduino, e può essere facilmente utilizzato in un qualunque sketch per il quale sia necessario temporizzare le operazioni definite nella funzione di loop(). 3.3.1 Design dello scheduler Nello stralcio di codice in figura 3.28 è definita una struttura rappresentante, nel contesto dell’algoritmo, un descrittore del task. Tale struttura consta di diversi campi descritti di seguito: • taskF_ptr *function: rappresenta un puntatore alla funzione che deve eseguire il task; • void *fun_args: sono gli argomenti, ossia i parametri della funzione al punto precedente; • int task_type: rappresenta il tipo di task: periodico, aperiodico, idle oppure non settato. • long period: è il periodo del task, espresso in millisecondi. 2 Earliest Deadline First, schedula i processi dinamicamente, relativamente alla deadline più imminente 41 3 – Descrizione e sviluppo del progetto • unsigned long nextJobTime: è compilato solo per task periodici, rappresenta il tempo di attivazione dell’istanza successiva del task; • int status: è lo stato del task, che può essere: pronto, in attesa del periodo, fine esecuzione o reset. • long release: definisce il release time, ossia il tempo di arrivo, del task. 1 typedef struct { 1 2 3 4 5 6 7 8 9 typedef struct { 2 taskF_ptr *function; void *fun_args; int task_type; long period; unsigned long nextJobTime; int status; long release; task_str task_list[MAX_TASK_NUM]; 3 4 5 }sched_str; 10 11 }task_str; Figura 3.28. Estratto del file myScheduler.h - Descrittore del task (a sinistra) e Struttura dello scheduler (a destra) La coda di scheduling è visualizzata come un vettore di descrittori di task. Tale è utilizzata al fine di definire il meccanismo di scelta del processo da eseguire. 1 /****** Funzioni di Utility ******/ 2 3 4 5 6 7 8 void task_str_reset(task_str*); void sched_init(sched_str*); void task_set(task_str*, taskF_ptr*, void*, long, int, long); int make_task_ready(task_str*, sched_str*); void sched_set_tasks(sched_str*); void _schedule(sched_str*); Figura 3.29. Estratto del file myScheduler.h - Procedure di Utility dello Scheduler L’insieme delle operazioni che devono essere effettuate per la configurazione e utilizzo dello scheduler sono riportate in figura 3.29 e di seguito vengono descritte nel dettaglio, anche in relazione al loro utilizzo: • void task_str_reset(task_str*): è una funzione che viene utilizzata per resettare il descrittore del task; tipicamente può essere utilizzata quando un task aperiodico termina la sua esecuzione o in fase di inizializzazione; • void sched_init(sched_str*): tale funzione è utilizzata per inizializzare la struttura di scheduling, e, in particolare, il vettore dei task della struttura sched_str descritta poc’anzi; • void task_set(task_str*, taskF_ptr*, void*, long, int, long): è una funzione utilizzata per settare le caratteristiche di un task attraverso il proprio descrittore (nell’ordine, il puntatore alla struttura del task, il puntatore alla funzione da eseguire, eventuali argomenti, il periodo, il tipo e il tempo di arrivo) . È richiamata prima dell’inserimento del task nella coda di scheduling; 42 3 – Descrizione e sviluppo del progetto • int make_task_ready(task_str*, sched_str*): è la funzione che, effettivamente, registra il task all’interno della coda di scheduling, registrandolo come pronto. Ritorna un valore che contrassegna l’esito dell’operazione; • void sched_set_tasks(sched_str*): è una operazione che viene effettuata per settare, in presenza di un task periodico, per la sua prima istanza, il valore della prima deadline assoluta, e quindi della sua prossima attivazione (analogamente, per task aperiodici definisce la deadline assoluta). • void _schedule(sched_str*): tale funzione richiama lo scheduler, e deve essere eseguita come unica azione all’interno della procedura di loop() di uno sketch Arduino. 3.3.2 Esempio di utilizzo Utilizzando l’architettura prototipale presentata nel paragrafo 2.2 e, allo stesso tempo, lo sviluppo del progetto presentato nella sezione 3.2.1 si costruisce di seguito una centrale che invia richieste periodicamente all’unico sensore (per motivi di testing) presente nel sistema. 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 void setup() { Serial.begin(9600); Serial.println("Gateway"); pinMode(SSerialTxControl, OUTPUT); pinMode(CSerialTxControl, OUTPUT); digitalWrite(SSerialTxControl, RS485Transmit); digitalWrite(CSerialTxControl, RS485Receive); Serial2.begin(4800); Serial3.begin(4800); initialize_addresses(&sensorAddresses); /*** ISTRUZIONE PER L’INIZIALIZZAZIONE DELLO SCHEDULER ***/ scheduler = (sched_str*)malloc(sizeof(sched_str)); myTask = (task_str*)malloc(sizeof(task_str)); sched_init(scheduler); task_set(myTask, (taskF_ptr*) fun , (void*)0, 2000, PERIODIC,0); make_task_ready(myTask, scheduler); sched_set_tasks(scheduler); } Figura 3.30. Estratto dello sketch (inizializzazione) Gateway.ino utilizzato come centrale nel testing dello scheduler 1 2 3 4 5 6 7 8 9 10 11 12 void fun(void* arg) { ... int type = getAddressType(address); if (type == ISSINGLE) sendSingleRequest(address); else ... } /********* Loop ***********/ void loop(){ _schedule(scheduler); } Figura 3.31. Estratto dello sketch Gateway.ino utilizzato come centrale nel testing dello scheduler 43 3 – Descrizione e sviluppo del progetto In figura 3.30 è rappresentata la funzione di inizializzazione del gateway che, in questo caso, lo si fa operare da centrale simulando sempre la richiesta dello stesso indirizzo. In particolare si nota l’insieme delle istruzioni che permettono l’inizializzazione dello scheduler; nel dettaglio è utile osservare la definizione di un unico task periodico di periodo pari a 2 secondi con tempo di arrivo pari a 0, a cui è assegnata la funzione fun, descritta in figura 3.31. Tutta la logica della centrale è quindi incapsulata all’interno della funzione fun. La procedura loop() si limita a richiamare la primitiva _schedule() dello scheduler, atta a verificare la presenza di eventuali task pronti e ad eseguirli nel rispetto dei vincoli. In figura 3.32 è presentato uno screenshot dell’output di esecuzione del Gateway utilizzato come centrale. Figura 3.32. Screenshot del monitor seriale del Gateway utilizzato come centrale 44 Capitolo 4 Conclusioni L’elaborato ha presentato un progetto volto allo sviluppo di una rete sensoriale, utilizzando sia tecnologie wired che wireless. A tal fine si è reso necessario introdurre diversi concetti preliminari, atti a fornire una base teorica alla realizzazione del sistema. In particolare, si è iniziato con un breve studio dei sistemi embedded, richiamando delle definizioni fondamentali, caratteristiche, fini e scopi di utilizzo. Si sono descritte problematiche di realizzazione hardware e di sviluppo software, caratterizzando le differenze rispetto ad un sistema general-purpose, considerando come esempio il sistema di antibloccaggio delle ruote (ABS) presente, ormai, in tutto il settore automotive. Si è osservato che il core di un sistema embedded è il microcontrollore e che, a seconda della complessità del sistema da realizzare, vadano utilizzati differenti livelli di sofisticatezza di esso. Si è citato una importante classe di sistemi integrati, ossia il settore real-time, che introduce vincoli temporale rispetto alle esecuzioni delle operazioni del sistema. Infine si sono considerati e classificati, a seconda della complessità del sistema, dei requisiti generali di sviluppo per i sistemi in questione. Con questa breve introduzione, si è passati allo studio della piattaforma Arduino. In particolare, a tal proposito, è stato presentato il prototipo fisico utilizzato per il testing del sistema, e si sono dettagliate le sue parti componenti da un punto di vista hardware. Tutto ciò è stato da introduzione per lo sviluppo effettivo del progetto, che è stato condotto con un approccio modulare a complessità crescente, considerando dapprima l’utilizzo di protocolli wired e, in un secondo momento, di protocolli wireless. La prima architettura presentata ha utilizzato un protocollo RS485, che come abbiamo potuto constatare, non definisce alcuna regola di comunicazione, ed è stato, quindi, compito dello sviluppatore definire un protocollo in tal senso. Lo stesso è stato utilizzato, con opportune modifiche, anche nello sviluppo wired tramite lo standard Ethernet e nello sviluppo wireless tramite lo standard 802.11. Si è presentata, infine, un’implementazione di uno scheduler per lo smistamento delle richieste sensoriali temporizzato ed un corrispondente esempio di utilizzo nel sistema progettato, evidenziandone l’efficacia. Per concludere, è importante sottolineare il fondamentale supporto di Arduino allo sviluppo embedded attraverso la definizione di un set di API organizzate in librerie. Con quanto specificato è bene constatare che non si è chiuso in alcun modo lo sviluppo del sistema, in quanto tale rimane comunque aperto a estensioni e ottimizzazioni. 45 Riferimenti [1] M. Wolf, " Computers as Components, Third Edition: Principles of Embedded Computing System Design ", Morgan Kaufmann, 2012. [2] M. Barr, " Programming Embedded Systems in C and C++ ", O’Reilly Media, 1st Ed., 1999. [3] F. Vahid & T. Givargis, " Embedded System Design: A Unified Hardware/Software Introduction ", John Wiley & Sons, 2nd Ed., 2003. [4] R. Kamal, " Embedded Systems: Architecture, Programming and Design ", McGraw-Hill, 2nd Ed., 2009. [5] G. Buttazzo, " Sistemi in tempo reale ", Pitagora Editrice, 3rd Ed., 2006. [6] Datasheet Atmel ATmega640/V-1280/V-1281/V-2560/V-2561/V, 2014. [7] W5500 Datasheet Version 1.0.6, 2013. [8] M10 Hardware Design, 2010. [9] ESP8266EX Datasheet Version 4.3, 2015. [10] ESNx5HVD08 Wide Supply Range RS-485 Transceiver, 2015. [11] Datasheet ATmega48A/PA/88A/PA/168A/PA/328/P, 2015. [12] Dispense Prof. Alessandro Cilardo. [13] Wikipedia - ABS, https://it.wikipedia.org/wiki/Sistema_anti_bloccaggio. [14] Arduino, http://arduino.org. [15] Quectel, http://www.quectel.com. [16] ESP8266-Comandi AT, http://www.electrodragon.com/w/index.php?title=Category:ESP8266. [17] Texas Instruments, http://www.ti.com. [18] TimerThree Library, https://github.com/PaulStoffregen/TimerThree. [19] Simple Scheduler Arduino, https://www.cyphar.com/blog/post/making-a-simple-scheduler-for-arduino 46