PrettyPrint

lunedì 19 luglio 2010

Timer AVR in 5min: 1) Partiamo da zero





Ultima revisione: 29/Apr/2012 



Il timer di un microcontrollore è una periferica hardware interna impiegata per la generazione, la misura, di tempi ed eventi, che, lavorando separatamente dall'esecuzione del programma, consente di ottenere una notevole efficienza e precisione rispetto alle tecniche software basate su cicli di ritardo.

Un timer è un registro a n bit, il cui dato viene incrementato di un'unità a ogni impulso di clock.

Il timer inizia così a contare da 0 e arriva fino a un numero massimo 2n-1, per poi ricominciare da zero e così via di nuovo fino al massimo numero (tutti bit a 1), continuamente.

Ad esempio, un elementare timer da 2bit conterà continuamente, in sequenza, quattro numeri:





0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3....


ossia in codice binario:




00, 01, 10, 11, 00, 01, 10, 11, 00, 01, 10, 11....


In questo caso, diremo anche che la risoluzione del timer è di 2bit.

La direzione del conteggio può anche essere verso il "basso", cioè dal numero più grande al numero più piccolo.

Un numero resta memorizzato nel registro per una durata di tempo pari a quella di un periodo di clock (Tc).

Per esempio, se Tc=0,25s, partendo dal primo zero e finquando il registro del timer conterrà il primo numero 3 sarà trascorso un tempo pari a 4 * Tc sec ossia 1s.

Dal successivo zero e fino a tutto il secondo numero tre verrà di nuovo impiegato 1s e così via, per il terzo tre...il comportamento è dunque periodico di periodo T

Il timer va in overflow, quando passa dal massimo numero rappresentable (registro con tutti bit a 1) allo zero.

Una possibile raffigurazione dell'esempio è riportata di seguito, nella quale ogni cella rappresenta il valore dell'ipotetico registro dati del timer che memorizza il numero:



In generale, il numero N al quale corrisponde un intervallo di tempo T, dato il tempo di clock Tc è dato da:




N.B. si aggiunge 1, perché anche lo zero "impegna" un ciclo di clock.

Possiamo sfruttare questo periodo di 1s per il nostro programma: per esempio, far compiere ogni secondo un'operazione: accendere o spegnere un LED oppure realizzare un orologio, per esempio ricorrendo a un'interruzione software.

Esempio:

Ogni sec:
{
   Accendi il LED sul pin PortB.2 se è spento, viceversa spegnilo se è acceso
}


oppure:

Ogni sec:
{
   secondi = secondi + 1;

   se (secondi==60) allora:
   {
      minuti = minuti + 1;
      secondi = 0;
   }

   se (minuti==60) allora:
   {
      ora = ora + 1;
      minuti = 0;
      secondi = 0;
   }

   se (ore==24) allora:
   {
      ore=0;
      minuti=0;
      secondi=0;
   }  
}


Un timer può essere usato non solo per schedulare eventi in uscita, come gli esempi fatti sopra, ma anche per contare eventi d'ingresso: quante volte è stato premuto un pulsante, quanto tempo è trascorso fra uno start e uno stop di un cronometro, la frequenza di un segnale, la durata di un impulso oppure il duty cicle di un segnale rettangolare.

In tal caso, cioè quando l'impulso di clock proviene dal mondo esterno, il nome più appropriato è quello di contatore. Perciò, nella letteratura si parla indistintamente di timer/contatore a secondo del particolare contesto d'utilizzo, se per creare tempi (out) o per contare eventi (in)


 I registri dell'AVR ATmega168

Un AVR ATmegax8 (48, 88, 168, 328) contiene tre timer:

Timer0, da 8 bit
Timer1, da 16 bit
Timer2, da 8 bit



Il registro dati del timer a 16bit è costituito dall'unione di due registri a 8bit: il primo che memorizza il byte inferiore e il secondo quello superiore.

Quindi il registro dati (che contiene il numero) del Timer1 sarà composto da due registri: TNCT1L e TCNT1H.

L'intero registro dati, in alcuni linguaggi, può comunque essere direttamente accessibile mediante l'etichetta TCNT1:

Una descrizione grafica molto usata per i timer è quella di rappresentare l'andamento nel tempo del numero contenuto nel registro dati

Nel caso del timer1 (TCNT1), che conti da 0 fino al suo valore massimo, il grafico assume un andamento "a rampa crescente":

Quando il timer conta da 0 fino al massimo valore dato dal numero di bit del suo registro dati, si dice che opera in modo normale o naturale; tale modalità è in effetti poco flessibile in quanto "rigidamente" legata al numero di bit e alla frequenza di clock; esistono invece diverse modalità e tecniche che consentono di controllare in modo preciso e affidabile i tempi generati, come si vedrà più avanti.






I contenuti di questo documento, sono stati resi possibili grazie ai seguenti strumenti gratuiti:
- LaTeX equation editor
- FidoCADJ (disegno grafici)
- Google Code Prettify (sorgenti con sintassi evidenziata)



mercoledì 30 giugno 2010

Pachube + Arduino & Ethernet Shield + LM35 - 2) Test

Il feed di prova è al seguente URL: http://www.pachube.com/feeds/8276

A quest'indirizzo: http://apps.pachube.com/ è possibile scaricare vari widget per diversi ambienti.

Alcuni dei widget, che consentono di incorporare in una pagina web il grafico della grandezza fisica che si sta monitorando:








Attraverso una console seriale (come quella dell'IDE di Arduino) è possibile visualizzare i messaggi inviati sulla seriale da Arduino.

-----
02-07-2010: il chip W5100 dello shield ethernet scalda parecchio, sembra essere un problema noto. Temo che il calore generato vada a influenzare, di qualche grado, la temperatura "catturata" dal sensore LM35 posto a pochi centimetri da Arduino+Shield

Pachube + Arduino & Ethernet Shield + LM35 - 1) Sketch

pachube_lm35.zip


NOTA BENE:

byte remoteServer[] = { 209,40,205,190 }; // IP del server pachube.com

non è più così ma:

byte remoteServer[] = { 173,203,98,29 }; // IP del server pachube.com

(grazie Weiss)



L'idea è quella di inviare la temperatura locale, misurata con il nostro sensore LM35, al server Pachube (http://www.pachube.com). Ciò avviene a intervalli regolari di tempo (1 min.) sfruttando come bridge di accesso alla rete il modulo Ethernet di Arduino, collegandolo al router ADSL domestico; questa soluzione evita, dunque, la necessità di avere un personal computer sempre acceso.

L'implementazione si basa sfruttando del codice prodotto da 'usman' (www.haque.co.uk), che ha reso disponibile gratuitamente degli sketch che consentono ad Arduino di dialogare con Pachube. Il lavoro di usman è di fatto riconosciuto da Pachube come l'insieme di API ufficiali per Arduino ( http://community.pachube.com/node/112 )

Gli sketch di usman sono due:

PACHUBE_FUNCTIONS.PDE

Contiene tutte le funzioni di accesso a Pachube, sia in get (lettura da Pachube di sensori remoti) che in put (scrittura su Pachube di sensori locali) . Sono state apportate alcune modifiche: la funzione pachube_in_out() accetta ora un parametro formale (tipo array di caratteri) che rappresenta il dato da inviare al server Pachube; cioè consente di migliorarne la modularità, in quanto tutto il lavoro di preparazione del dato da inviare viene fatto nello sketch chiamante, principale, non modificando dunque questo file di libreria. Inoltre, dalla versione 18 di Arduino, essendo disponibile anche la stampa dei numeri in virgola mobile è stata prevista questa possibilità nell'istruzione di lettura dei sensori remoti.


ETHERNET_FUNCTIONS.PDE


Contiene le funzioni necessaria per l'inizializzazione dello shield Ethernet di Arduino (versione ufficiale). A questo sketch, come il precedente, non va apportata alcuna modifica.

Il terzo sketch è quello principale, il main: PACHUBE_LM35.PDE

In quest'ultimo si rivede la funzione per la lettura della temperatura con un LM35, già usata nel precedente lavoro. Il dato letto dalla nostra funzione viene però scomposto nella parte intera e nella parte decimale e poi riunito (con il punto decimale di separazione) dalla funzione sprintf() in un'unica stringa; questo si rende necessario perché passando direttamente il valore, su Pachube questo non viene formattato correttamente. La stringa così generata viene passata a pachube_in_out().

Tutti e tre gli sketch devono trovarsi nella stessa directory.

La configurazione dei parametri relativi a Pachube viene affidata a etichette simboliche:

#define SHARE_FEED_ID              8276
#define REMOTE_FEED_ID             0   
#define REMOTE_FEED_DATASTREAMS    0   
#define PACHUBE_API_KEY            "INSERIRE_QUI_LA_PROPRIA_API_KEY"
#define UPDATE_INTERVAL            60000


L'etichetta SHARE_FEED_ID rappresenta l'ID che Pachube ha assegnato all'atto della creazione del nostro feed manuale. Nel caso in esame, lo sketch funziona solo in modalità di "sharing" (invia dati, non legge sensori remoti), quindi REMOTE_FEED_ID e REMOTE_FEED_DATASTREAMS vanno posti a 0.

Nell'etichetta PACHUBE_API_KEY va inserire la chiave API che troviamo nell'area utente.

Va inserita fra le doppie virgolette.

In ultimo, l'intervallo di aggiornamento: UPDATE_INTERVAL che si è impostato a 1min (60.000 ms).

Una seconda parte, riguarda i parametri per la connessione di rete del modulo Ethernet,
che sono invece memorizzati in delle variabili:

byte mac[] = { 0xCC, 0xAC, 0xBE, 0xEF, 0xFE, 0x91 };
byte ip[] = { 192, 168, 0, 10  };               
byte gateway[] = { 192, 168, 0, 1 };           
byte subnet[] = { 255, 255, 255, 0 };           
byte remoteServer[] = { 173,203,98,29 }; 


mac[] e remoteServer[] non vanno modificati...il resto, dipende dalla configurazione del proprio router...





/*
 * Nome sketch:        PACHUBE_LM35.PDE
 *                     (ETHERNET_FUNCTIONS.PDE, PACHUBE_FUNCTIONS.PDE)
 * Motivazione:        Acquisisce una temperatura mediante un sensore LM35 collegato ad Arduino e, 
 *                     tramite il modulo Ethernet dello stesso collegato a un router ADSL, 
 *                     la invia su un server Pachube a intervalli regolari.
 * Data:               30-giu-2010                   
 * Rev.:               1.1
 * Autore:             Francesco Parisi, fparisi [at] gmail [dot] com , fparisi [at] tiscali [dot] it
 * Ver. Arduino:       18 (o superiore)
 *
 * Note:               Il presente sketch utilizza (e si basa) su sketch sviluppati da usman (www.haque.co.uk), maggio 2009
 *                     copy, distribute, whatever, as you like: 
 *                     http://community.pachube.com/node/112 
 *
*/

#include <Ethernet.h>
#include <string.h>
#include <stdio.h> // per la funzione sprintf

////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* Configurazione connessione al server Pachube */
#define SHARE_FEED_ID              8276     // ID del feed locale Pachube (quello assegnato da Pachube)
#define REMOTE_FEED_ID             0        // ID del feed remoto Pachube (impostare a 0 se non si usa nessun feed remoto)
#define REMOTE_FEED_DATASTREAMS    0        // Numero di sensori remoti associati al feed remoto (impostare a 0 se non si usa nessun feed remoto)
#define PACHUBE_API_KEY            "INSERIRE_QUI_LA_PROPRIA_API_KEY"

#define UPDATE_INTERVAL            60000    // Se la connessione è valida aspetta 10s prima di un nuovo aggiornamento - dovrebbe essere non inferiore a 5s
#define RESET_INTERVAL             10000    // Se la connessione fallisce/si resetta aspetta 10s prima di riprovare - dovrebbe essere non inferiore a 5s
float remoteSensor[REMOTE_FEED_DATASTREAMS];    // Se i dati sono diversi (es. interi) cambiare il tipo di ritorno    
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* Configurazione hardware di Arduino */
#define LM35PIN                    5        // Numero dell'ingresso analogico al quale e' collegato il sensore LM35
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* Configurazione di rete del modulo Ethernet */
byte mac[] = { 0xCC, 0xAC, 0xBE, 0xEF, 0xFE, 0x91 }; // assicurarsi che il MAC address sia unico sulla propria rete
byte ip[] = { 192, 168, 0, 10  };                    // dipende dalla configurazione del ns. router
byte gateway[] = { 192, 168, 0, 1 };                 // dipende dalla configurazione del ns. router
byte subnet[] = { 255, 255, 255, 0 };                // dipende dalla configurazione del ns. router
byte remoteServer[] = { 173, 203, 98, 29 };            // IP del server pachube.com
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/* Funzione per leggere la temperatura rilevata dall'LM35 */
double readLM35Temp()
{
double temp = 0.0;       // valore convertito in temperatura (°C)
int val = 0;             // valore quantizzato dall'ADC [0..1023]
int nread = 20;          // numero di letture 
double somma = 0.0;      // somma delle singole letture

for (int i=0; i<nread; i++)
{
val = analogRead( LM35PIN );               // legge il dato convertito della tensione sul pin LM35PIN 
temp = ( 100.0 *  1.1 * val ) / 1024.0;    // Vref=1,1V (riferimento interno)
somma += temp;                             // lo aggiunge alla somma delle temperature lette   
delay( 50 );                               // aspetta 50ms  
}
return  ( somma / nread );                 // restituisce la temperatura media 

}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


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

analogReference( INTERNAL );    // per l'ADC usiamo il Vref interno da 1,1V (migliore precisione)
analogRead( LM35PIN );          // Prima lettura "a vuoto" (cfr. pag. 47 datashett ATmega168)
} 
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void loop()
{

char data[20];  // Stringa dati da inviare a Pachube

// Leggiamo la temperatura e assegnamone il risultato a 'temp'
double temp = readLM35Temp();

// Estraiamo la parte intera della temperatura
unsigned int int_part = (unsigned int)temp;

// quindi estraiamo la parte decimale (con una cifra dopo la virgola)
unsigned int frac_part = 10.0 * ( temp - (double)int_part );

// Creiamo la stringa concatenando le due parti
sprintf( data, "%d.%d", int_part, frac_part );

// Chiamiamo la funzione di put/get verso/da Pachube
pachube_in_out ( data );


}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////







Di seguito, i due sketch di usman:


PACHUBE_FUNCTIONS.PDE



/*
 * Nome sketch:        PACHUBE_FUNCTIONS.PDE
 * Motivazione:        Gestisce il put/get dei dati verso/da il server Pachube
 *                     attraverso un'unica funzione di put/get. I dati letti dai sensori
 *                     remoti vengono stampati su seriale.
 * Data:               30-giu-2010                   
 * Rev.:               1.1
 * Autore:             usman (www.haque.co.uk), maggio 2009
 *                     http://community.pachube.com/node/112 
 * Modificato da:      Francesco Parisi (fparisi * gmail . com, giugno 2010) 
 *                     + La funzione pachube_in_out() ha un parametro di ingresso
 *                     + La stampa dei dati dei sensori remoti avviene con due cifre dopo la virgola 
 * Ver. Arduino:       18 (o superiore)
 *  
*/

boolean found_status_200 = false;
boolean found_session_id = false;
boolean found_CSV = false;
char *found;
unsigned int successes = 0;
unsigned int failures = 0;
boolean ready_to_update = true;
boolean reading_pachube = false;

boolean request_pause = false;
boolean found_content = false;

int content_length;

unsigned long last_connect;


void pachube_in_out( char data[] ){

content_length = strlen( data );

if (millis() < last_connect) last_connect = millis();

if (request_pause){
if ((millis() - last_connect) > interval){
ready_to_update = true;
reading_pachube = false;
request_pause = false;
found_status_200 = false;
found_session_id = false;
found_CSV = false;

//Serial.print("Ready to connect: ");
//Serial.println(millis());
}
}

if (ready_to_update){
Serial.println("Connecting...");
if (localClient.connect()) {

Serial.println("GET request to retrieve");

localClient.print("GET /api/");
localClient.print(REMOTE_FEED_ID);
localClient.print(".csv HTTP/1.1\nHost: pachube.com\nX-PachubeApiKey: ");
localClient.print(PACHUBE_API_KEY);
localClient.print("\nUser-Agent: Arduino (Pachube In Out v1.1)");
localClient.println("\n");

Serial.println("finished GET now PUT, to update");

localClient.print("PUT /api/");
localClient.print(SHARE_FEED_ID);
localClient.print(".csv HTTP/1.1\nHost: pachube.com\nX-PachubeApiKey: ");
localClient.print(PACHUBE_API_KEY);

localClient.print("\nUser-Agent: Arduino (Pachube In Out v1.1)");
localClient.print("\nContent-Type: text/csv\nContent-Length: ");
localClient.print(content_length);
localClient.print("\nConnection: close\n\n");
localClient.print(data);

localClient.print("\n");

Serial.println("\n--- updated: ");
Serial.println(data);

ready_to_update = false;
reading_pachube = true;
request_pause = false;
interval = UPDATE_INTERVAL;

// Serial.print("finished PUT: ");
// Serial.println(millis());

} 
else {
Serial.print("connection failed!");
Serial.print(++failures);
found_status_200 = false;
found_session_id = false;
found_CSV = false;
ready_to_update = false;
reading_pachube = false;
request_pause = true;
last_connect = millis();
interval = RESET_INTERVAL;
setupEthernet();
}
}

while (reading_pachube){
while (localClient.available()) {
checkForResponse();
} 

if (!localClient.connected()) {
disconnect_pachube();
}
} 
}

void disconnect_pachube(){
Serial.println("disconnecting.\n=====\n\n");
localClient.stop();
ready_to_update = false;
reading_pachube = false;
request_pause = true;
last_connect = millis();
found_content = false;
resetEthernetShield();
}


void checkForResponse(){  
char c = localClient.read();
//Serial.print(c);
buff[pointer] = c;
if (pointer < 64) pointer++;
if (c == '\n') {
found = strstr(buff, "200 OK");
if (found != 0){
found_status_200 = true; 
//Serial.println("Status 200");
}
buff[pointer]=0;
found_content = true;
clean_buffer();    
}

if ((found_session_id) && (!found_CSV)){
found = strstr(buff, "HTTP/1.1");
if (found != 0){
char csvLine[strlen(buff)-9];
strncpy (csvLine,buff,strlen(buff)-9);

//Serial.println("This is the retrieved CSV:");     
//Serial.println("---");     
//Serial.println(csvLine);
//Serial.println("---");   
//   Serial.println("\n--- updated: ");
//  Serial.println(pachube_data);
Serial.println("\n--- retrieved: ");
char delims[] = ",";
char *result = NULL;
char * ptr;
result = strtok_r( buff, delims, &ptr );
int counter = 0;
while( result != NULL ) {
remoteSensor[counter++] = atof(result); 
result = strtok_r( NULL, delims, &ptr );
}  
for (int i = 0; i < REMOTE_FEED_DATASTREAMS; i++){
Serial.print( remoteSensor[i], 2); // due cifre dopo la virgola
Serial.print("\t");
}

found_CSV = true;

Serial.print("\nsuccessful updates=");
Serial.println(++successes);

}
}

if (found_status_200){
found = strstr(buff, "_id=");
if (found != 0){
clean_buffer();
found_session_id = true; 
}
}
}





ETHERNET_FUNCTIONS.PDE

/*
 * Nome sketch:        ETHERNET_FUNCTIONS.PDE
 * Motivazione:        Gestisce la configurazione 
 *                     e l'inizializzazione del dispositivo Ethernet
 * Data:               30-giu-2010                   
 * Rev.:               1.1
 * Autore:             usman (www.haque.co.uk), maggio 2009
 *                     http://community.pachube.com/node/112 
 * Ver. Arduino:       18 (o superiore)
 *  
*/

Client localClient(remoteServer, 80);
unsigned int interval;

char buff[64];
int pointer = 0;

void setupEthernet(){
resetEthernetShield();
Client remoteClient(255);
delay(500);
interval = UPDATE_INTERVAL;
Serial.println("setup complete");
}

void clean_buffer() {
pointer = 0;
memset(buff,0,sizeof(buff)); 
}

void resetEthernetShield(){
Serial.println("reset ethernet");
Ethernet.begin(mac, ip);
}

lunedì 15 febbraio 2010

LM35+LCD+web+MySQL - 7) Visualizzazione in tempo reale;problemi noti

In ultimo ci rimane da creare una pagina web in PHP che ci visualizzi la temperatura in tempo reale e il grafico generato da plotter.

Ogni qualvolta si apre la pagina web temp.php, viene richiamata la funzione fetchTemp() precedentemente vista, che stampa la temperatura misurata in quel momento.

Comunque ogni 30sec. viene forzato il refresh della pagina e quindi viene rieseguita una nuova lettura.

Infine, lo script plotter.php viene richiamato nella pagina come immagine e il risultato è quello di visualizzare il grafico con temperature rilevate nelle ultime 24hh

TEMP.PHP



<?
require_once('fetcher.php')
/*************************************************************************************************
*
* Motivazione: visualizza a ogni refresh della pagina (e comunque in automatico ogni 30 secondi)
* il valore attuale di temperatura acquisito da Arduino.
* Inoltre, richiama plotter per creare un grafico con temperature delle ultime 24hh
*
* Autore : Francesco Parisi (fparisi at tiscali dot it) , (fparisi at gmail dot com)
*
* Richiede : fetcher.php
*
*************************************************************************************************/
?>

<html>

<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Cache-Control" content="no-cache">
<meta http-equiv="Refresh" content="30; url="temp.php">
<title>Termometro Web</title>
</head>

<body>
<h1 align="center">Termometro Web</h1>
<center><table border="1" cellpadding="10" cellspacing="10" bgcolor="black">
<tr><td><font color="#00ff00" size="5"><b>

<?php echo fetchTemp() ?>
&nbsp;&deg;C

</font></b>
</td></tr></table></center>

<br><br><br><br>

<p align="center">
<img src="plotter.php" border=0>
</p>

<br clear="all">

<i>Autore: Francesco Parisi<br>Versione: 1.5, U.R. 03-Feb-2010</i>

</body>

</html>





Il risultato nel browser è il seguente (click sopra per ingrandire):







PROBLEMI NOTI


Se durante la fase dell'upload dello sketch avviene anche la richiesta della temperatura (a mezzo di fetcher) il flusso seriale va in stallo...lo script perl non riesce a disimpegnare le risorse allocate; ciò produce un consumo delle risorse pari al 100% da parte del relativo processo e l'unico modo è killarlo da shell.

Dall'altro lato non si riesce a caricare su Arduino lo sketch ottenendo degli errori da parte di avrdude.

Quindi l'unico modo per evitare questo è cercare di non rimanere aperta nel browser la pagina temp.php (o comunque metterla off-line, insieme allo script logger) durante i momenti di upgrade del firmware di Arduino.

Buon divertimento

LM35+LCD+web+MySQL - 6) Plotter

Per la creazione del grafico si è usata la libreria JpGraph (http://www.aditus.nu/jpgraph/) che consente di produrre grafici in PHP in modo molto semplice e flessibile.

Su Ubuntu la libreria si installa comodamente da riga di comando:

$ sudo apt-get install libphp-jpgraph


tutti i file della libreria vengono così installati nella directory /usr/share/jpgraph/


PLOTTER.PHP




<?php
/*************************************************************************************************
*
* Motivazione: costruisce un grafico con temperature rilevate nelle ultime 24hh
*
* Autore : Francesco Parisi (fparisi at tiscali dot it) , (fparisi at gmail dot com)
*
* Richiede : db.php, jpgraph.php, jpgraph_line.php
*
*************************************************************************************************/

require_once ('/usr/share/jpgraph/jpgraph.php');
require_once ('/usr/share/jpgraph/jpgraph_line.php');
require_once ('db.php');


// Apre la connessione al server
$conn = mysql_connect( $host, $user, $pass )
or die('Impossibile connettersi al server: ' . mysql_error());


// Seleziona il database
$sel = mysql_select_db( $dbname, $conn )
or die('Impossibile selezionare il database: ' . mysql_error());

// Temperature ultime 24hh
$sql_last_24hh = 'SELECT * FROM `log` WHERE `time` >= NOW() - INTERVAL 1 DAY';
$res = mysql_query( $sql_last_24hh )
or die('Impossibile eseguire query: ' . mysql_error());

// Formatta il timestamp e costruisce gli assi per il grafico
if ( $row = mysql_fetch_assoc($res) or die(mysql_error()) )
{
do
{
// Converte il timestamp MySQL in timestamp PHP
$timestamp = strtotime($row['time']);

// Costruisce il vettore degli orari (asse x)
$x[] = sprintf("%s, %s", date ('j/n', $timestamp), date ('H:i', $timestamp) );

// Costruisce il vettore delle temperature (asse y)
$y[] = $row['temp_int'];

} while ( $row=mysql_fetch_assoc($res) );
}

// Crea il grafico
$graph = new Graph(1024, 800,"auto");
$graph->SetScale( "textlin");
$graph->img-> SetMargin(60,40,40,110);
$graph->title->Set( 'Temperature rilevate nelle ultime 24hh');
$graph->title->SetFont( FF_FONT2, FS_BOLD );
$graph->xaxis->SetTickLabels( $x );
$graph->xgrid->Show(true);
$graph->xaxis->SetLabelAngle(90);
$graph->SetShadow();

// Crea il plot
$lineplot = new LinePlot( $y );
$lineplot->value->Show();
$lineplot->SetColor("blue");
$lineplot->SetWeight(2); // Two pixel wide
$lineplot->value->SetColor("darkred");
$lineplot->value->SetFormat( "%0.1f");
//$lineplot->mark ->SetType(MARK_FILLEDCIRCLE);

// Aggiunge il plot al grafico
$graph->Add( $lineplot );

// Mostra il grafico
$graph->Stroke();

// Chiude connessione
mysql_close( $conn )
or die('Impossibile chiudere connessione: ' . mysql_error());
?>



Ecco un esempio di grafico generato (click sopra per ingrandire), che verrà poi richiamato nella pagina web:

LM35+LCD+web+MySQL - 5) Logger,mysql e cron

logger è lo script incaricato di leggere la temperatura (mediante fetcher) e inserirla nel server MySQL. Questo script viene lanciato in automatico da cron ogni mezz'ora.

Quindi si crea prima la il database con la tabella sql, poi lo script e infine si configura cron.

Il codice sql per creare il database e la tabella che conterrà orario e temperatura interna sarà:
LOG.SQL

CREATE DATABASE `temperature` ;

CREATE TABLE `temperature`.`log` (
`id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`time` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
`temp_int` FLOAT NOT NULL
) ENGINE = MYISAM ;

Lo script logger.php va salvato in una directory diversa da quella html pubblica proprio perché non deve essere richiamabile via web.

Personalmente li ho salvati nella cartella utente /home/frank ma si potrebbe pensare anche alla directory /usr/local/bin:

LOGGER.PHP


<?
/*************************************************************************************************
*
* Motivazione: memorizza in un database MySQL il valore di temperatura acquisito da Arduino.
*
* Autore : Francesco Parisi (fparisi at tiscali dot it) , (fparisi at gmail dot com)
*
* Richiede : db.php, fetcher.php
*
*************************************************************************************************/

require_once('fetcher.php');
require_once('db.php');

// Apre la connessione al server
$conn = mysql_connect( $host, $user, $pass )
or die('Impossibile connettersi al server: ' . mysql_error());

// Seleziona il database
$sel = mysql_select_db( $dbname, $conn )
or die('Impossibile selezionare il database: ' . mysql_error());

// Legge da Arduino la temperatura
$temperature = fetchTemp();

// Query di inserimento
$sql=("INSERT INTO `log` (`id` ,`time` ,`temp_int`)
VALUES (NULL, NULL, ". $temperature .")");

// Esegue la query
$res = mysql_query( $sql )
or die('Impossibile eseguire query: ' . mysql_error());


// Chiude connessione
mysql_close( $conn )
or die('Impossibile chiudere connessione: ' . mysql_error());


?>



Nel file logger.php viene incluso un file db.php, che contiene i dati di accesso al server MySQL. Sostituire con i propri parametri:

DB.PHP



<?php
$host = 'localhost';
$dbname = 'temperature';
$user = 'root';
$pass = 'tua_password_qui';
?>







Infine, si configurerà cron per lanciare il comando:

php /home/frank/logger.php > /dev/null 2>&1


ogni mezz'ora.

Da shell, lanciare

$ crontab -e


Apparirà l'editor di cron per l'utente corrente. Aggiungere la seguente riga:

*/30 * * * * php /home/frank/logger.php # logga temperatura


Salvare il file e uscire dall'editor testo

Assicurarsi che il compito sia stato correttamente inserito:


$ crontab -l


Non ci resta ora che la creazione del grafico e una pagina web che mostri la temperatura corrente e il grafico stesso