I microcontrollori della famiglia HCS12 prodotti da Freescale (ora NXP) sono dei micro a 16 bit, versione potenziata dei 68HC11 di Motorola. In questo articolo vedremo a livello generale l’implementazione del web server su un componente di questa famiglia, il micro MC9S12NE64. Tale componente è stato scelto perché possiede un controller Ethernet integrato compatibile 802.3/802.3u da 10/100 Mbps Il microcontrollore è dotato inoltre di 64KB di FLASH EEPROM, 8KB di RAM, ed include diverse altre periferiche tra cui due SCI, una SPI, una IIC ed un convertitore A/D a 10 bit.
LO STACK TCP/IP CMX-MICRONET
Lo stack TCP/IP
Lo stack TCP/IP, nato alla fine degli anni ‘70 per definire un insieme di protocolli di comunicazione per la rete della Difesa americana ARPAnet, è diventato in seguito lo standard sia di Internet che delle reti locali. È solito associare i livelli che costituiscono lo stack ai corrispondenti livelli definiti dallo standard internazionale OSI (Open Systems
Interconnection). Questo standard è utilizzato come riferimento per comprendere l’architettura di altri modelli: ciò è dovuto principalmente alla chiara distinzione che effettua tra servizi, interfacce e protocolli
- servizi: l’insieme delle operazioni che un livello offre al livello superiore
- interfaccia: specifica come i processi del livello superiore possono richiedere i servizi (parametri da passare, valori ritornati)
- protocollo: un insieme di regole che stabiliscono come due nodi si scambiano informazioni attraverso la rete.
Lo stack TCP/IP è suddiviso nei 4 livelli seguenti:
- Livello delle Applicazioni: definisce i protocolli utilizzati dalle applicazioni e gestisce la presentazione, la codifica ed il processo di scambio delle informazioni. I protocolli più noti sono HTTP (HyperText Transfer Protocol) -il protocollo del Web- FTP (File Transfer Protocol), SMTP (Simple Mail Transfer Protocol), Telnet.
- Livello di Trasporto: realizza una connessione logica tra l’host sorgente e l’host destinazione. A questo livello i dati prodotti dalle applicazioni vengono segmentati in trasmissione e riassemblati in ricezione. I protocolli definiti sono TCP e UDP. TCP (Transmission Control Protocol) è un protocollo connesso che assicura l’affidabilità del trasporto delle informazioni tramite l’uso di numeri di sequenza dei segmenti inviati e dell’acknowledgement della ricezione. UDP (User Datagram Protocol) è invece un protocollo non connesso, in cui cioè ogni segmento ha vita a sè stante e non viene assicurata né la correttezza né l’ordine corretto di consegna dei segmenti.
- Livello Internet: il suo compito principale è quello di determinare il percorso che i dati devono seguire per giungere a destinazione. A questo livello i dati vengono suddivisi in pacchetti (packets) e vengono associati gli indirizzi logici (indirizzi IP) agli host sorgente e destinazione. Il protocollo principale è IP (Internet Protocol): è un protocollo non connesso il cui compito è quello di instradare i pacchetti utilizzando il percorso migliore possibile in un dato momento. Altri protocolli che operano a questo livello sono ICMP (Internet Control Message Protocol), utilizzato per effettuare la risoluzione di problemi sulla rete, ARP (Address Resolution Protocol) che associa all’indirizzo logico IP l’indirizzo fisico dell’host (indirizzo MAC).
- Livello Host to Network: in realtà TCP/IP non fa alcuna supposizione particolare sui protocolli offerti da questo livello ma utilizza quelli normalmente disponibili purchè in grado di inviare pacchetti IP sulla rete. Ad esempio, nell’ambito delle reti locali opera su Ethernet/IEEE802.3, Wireless Ethernet/IEEE802.11 e molti altri; nell’ambito delle reti geografiche su HDLC, PPP, PPPoE ed altri.
Come visto, il livello Host to Network non si occupa di stabilire quali siano i protocolli che permettono il collegamento logico e la comunicazione fisica dei dati tra i nodi. Può essere allora utile adottare un modello ibrido nel quale vengano rappresentati anche i livelli relativi a queste funzioni:
- Livello Data Link: a questo livello gli indirizzi logici IP vengono associati agli indirizzi fisici MAC dei nodi, vengono inoltre gestite le politiche di accesso al mezzo trasmissivo ed il controllo di flusso dei dati.
- Livello Fisico: è il livello che si occupa della trasmissione fisica dei bit, di stabilire se la trasmissione è half/full duplex, del timing e di tutte le caratteristiche costruttive delle interfacce.
Nella tabella 1 vengono riassunti i modelli ISO/OSI e TCP/IP, accanto ai protocolli principali.
Lo stack TCP/IP mette a disposizione delle applicazioni 3 interfacce software (API):
- Application API, che fornisce servizi per implementare applicazioni HTTP, FTP ecc.
- Socket API, tra il livello trasporto ed i livelli superiori
- Device Driver API: driver delle schede di rete dei modem o delle applicazioni software, che operano al livello Network dello stack.
Un’applicazione che utilizza lo stack TCP/IP, in trasmissione userà le funzioni del livello inferiore e così via fino al livello Network. In ricezione, dal livello inferiore (device driver) viene invocato un interrupt, mentre dai livelli superiori sono utilizzati dei meccanismi di callback. La figura 1 schematizza tale modello di funzionamento.
L’IMPLEMENTAZIONE DELLO STACK TCP/IP CMX-MICRONET
Lo stack TCP/IP CMX-MicroNet è stato progettato specificamente per architetture a microcontrollore ad 8/16 bit con limitate risorse di memoria. Sono disponibili versioni dello stack per diverse famiglie di micro di Freescale tra cui HCS08, HCS12 e recentemente anche per i più potenti micro a 32 bit della famiglia Coldfire. Tra le caratteristiche principali dello stack:
- scritto interamente nel linguaggio C;
- supporta direttamente Web server, chiamate CGI (Common Gateway Interface) per realizzare pagine dinamiche, può gestire applet Java;
- viene eseguito sia stand-alone che integrato con un RTOS prodotto da CMX o da terze parti;
- supporta fino a 127 socket contemporanei.
Per limitare le dimensioni del codice, lo stack presenta diverse limitazioni rispetto allo standard TCP/IP, limitazioni che tuttavia sono comuni a molte implementazioni dello stack per microcontrollori; le principali sono:
- non supporta la frammentazione dei pacchetti IP (i server HTTP ed FTP non utilizzano comunque la frammentazione);
- ICMP supporta solo Echo Reply;
- TCP utilizza una sliding window a larghezza fissa;
- ad ogni pacchetto TCP bisogna rispondere con un ACK prima di poterne ricevere un altro.
Lo stack mette a disposizione una API (Application Programming Interface) per lo sviluppo delle applicazioni. Le categorie di funzioni della API che ci interessano maggiormente per la realizzazione di un web server sono:
- Funzioni Ethernet: inizializzano il controller Ethernet, ricavano l’indirizzo IP tramite il protocollo BOOTP. Forniscono supporto al driver Ethernet integrato nello stack. Alcuni dei parametri usati vengono definiti nei file di configurazione ether_init.h ed emac_fifo_cfg.h.
- Funzioni socket: simili alle funzioni standard BSD, permettono di usare socket sia Ethernet che PPP/SLIP.
- Funzioni TCP: normalmente tali funzioni non vengono invocate direttamente ma tramite le funzioni socket .
- Funzioni server: realizzano un server HTTP o FTP richiamando a loro volta delle funzioni specifiche HTTP o FTP. Sono disponibili solo se non viene usato un RTOS.
- Funzioni HTTP: inizializzano il server HTTP, gestiscono i comandi GET e POST e processano i packet ricevuti.
- Funzioni di callback: sono funzioni previste per essere application-dependent, e sono richiamate dalle funzioni TCP e server per gestire le elaborazioni dei packet ricevuti o realizzare le operazioni di cleanup al termine dell’invio di dati. Ancora, possono essere utilizzate per definire delle operazioni che il server può svolgere quando non è impegnato ad elaborare dei packet.
Funzioni del Virtual File System: funzioni che servono per aggiungere, leggere od eliminare i file che costituiscono l’applicazione web al file system virtuale. Nel caso si utilizzi la memoria FLASH per i file, le funzioni relative permettono di gestirla opportunamente. Altre funzioni servono per associare dei puntatori alle funzioni CGI (GET e POST).
Nella tabella 2 viene fatta una descrizione più approfondita delle funzioni principali usate nella realizzazione del web server d’esempio.
GENERALITA' SULLA REALIZZAZIONE DI UN WEB SERVER
A titolo esemplificativo, vedremo ora un progetto di una applicazione web. Innazitutto è necessario installare lo stack CMX per poi compilare la propria applicazione con CodeWarrior per esempio, e testarla attraverso una scheda di sviluppo come la EVB9S12NE64. In figura 2 un progetto di esempio caricato nell’IDE di CodeWarrior.
L’applicazione web
Le pagine web che costituiscono la nostra applicazione possono essere create con i soliti strumenti come Frontpage o anche un semplice editor di testo. Queste, unitamente alle immagini ed agli altri file - applet Java, file Flash e quant’altro – vanno successivamente convertiti ciascuno in due file .c e .h tramite la utility HTML2C. Aggiungeremo quindi i file .c ai file del progetto in CodeWarrior ed includeremo i file .h nel codice dell’applicazione. L’applicazione d’esempio realizza una pagina web dinamica suddivisa in due frame, che utilizza due server-side include (comandi HTTP GET), un form ed un applet Java. I server-side include sono un metodo per inserire valori dinamici in una pagina web: in pratica si utilizza un tag HTML speciale come nell’esempio seguente:
<HTML> <BODY text="#000000" vlink="#990099" alink="#990099" bgcolor="#FFFFFF" link="#0000CC"> <TABLE> <TR> <TD width="50%"><FONT FACE="Arial" size="+1"> Il Form seguente viene usato per inviare al server web un nuovo valore per la variabile my_var: immettendo un nuovo valore nel campo testo e cliccando Set la funzione POST <I>post_var_func()</I> aggiornera' la variabile; la funzione server-side include <I>get_var_func()</I> visualizzera' quindi il nuovo valore. </FONT> </TD> <TD align="center"><FONT FACE="Arial" SIZE="+1" COLOR=RED><B>JAVA APPLET</B></FONT><BR/> <FONT FACE="Arial" SIZE="+1" color="BLUE">L'applet Java apre un socket TCP sulla porta 2000 mediante il quale riceve dal server il valore dei ticks del timer ogni 5 secondi. </FONT> </TD> </TR> <TR> <TD width="50%" align="center"><BR> <FORM NAME="POST_TEXT" ACTION="postVar" METHOD=POST> <B><FONT FACE="Arial">my_var value: </FONT><FONT FACE="Arial" COLOR="RED"><!--#exec cgi="getVar"--></FONT></B><BR/> <INPUT NAME="var_name" TYPE="Text" MAXLENGTH=4><BR/> <INPUT TYPE="Submit" VALUE="Set"> </FORM> </TD> <TD align="center"> <APPLET code="web2demo.class" width="300" height="80"> <param name=Tick value="<!--#exec cgi="getTickVal"-->"> </APPLET> </TD> </TR> </TABLE> </BODY> </HTML>
Listato 1 |
<!—#exec cgi=”getVar” —>
questo tag indica al server di eseguire la funzione corrispondente a getVar nel file system virtuale, quindi il risultato dell’esecuzione della funzione verrà inserito nella pagina web al posto del tag. Ciò viene utilizzato nell’esempio per ricavare il valore di una variabile in memoria. I Form vengono gestiti tramite funzioni POST: nell’esempio seguente
<FORM NAME=”POST_TEXT” ACTION=”postVar” METHOD=POST>
il valore dell’attributo ACTION indica al server di eseguire la funzione corrispondente a postVar nel file system virtuale. Dopo aver effettuato le elaborazioni sui valori ricevuti, la funzione POST risponderà al server inviando un messaggio oppure inviando un file.
L’importanza dell’uso di un applet Java consiste nel fatto che questo è in grado di stabilire una connessione sempre attiva con il browser, realizzando così una applicazione del tipo server push con la quale i dati vengono inviati dal server quando vengono modificati. Nel nostro esempio l’applicazione apre un socket sulla porta 2000 che si pone in attesa di richieste di connessione: l’applet Java sul browser apre a sua volta una connessione TCP con destinazione la porta 2000. Ogni 5 secondi il server invierà allora il valore dei ticks del timer di sistema (ricavato dalla macro MN_GET_TICK) a tutti i socket aventi destinazione la porta 2000. L’applet Java viene aggiunto nella pagina web come segue:
<applet code=”web2demo.class” width=”300”
height=”80”>
<param name=Tick
value=”<!—#exec cgi=”getTickVal”—>”>
Si noti che il valore del parametro Tick da passare all’applet, viene ricavato dalla funzione GET corri- spondente a getTickVal (nell’esempio sarà la fun- zione get_tick_func()).
In figura 3 possiamo vedere l’applicazione web in esecuzione.