Riprendendo il progetto di SismoArduino ho voluto anche qui provare a migliorare le prestazioni del passo di campionamenti
Le modifiche hanno riguardato sostanzialmente la riduzione dei tempi di ritardo tra le letture e l'uso di un pc piu' perfomante rispetto a quello usato in precedenza
Con queste modifiche il passo di campionamento e' salito ad 1 campione ogni 7 millisecondi in linea con quanto provato con SismoYUN (a questo punto questo puo' essere il limite della scheda senza entrare in modalita' free running)
---------------------------------------------------- #include <SPI.h> #include <Ethernet.h> #include <EthernetUdp.h> //Arduino byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; IPAddress ip(192,168,1,2); unsigned int localPort = 8888; //Server byte remoteIP[4] = {192,168,1,1}; int remotePort = 5005; String dati; char charBuf[1024]; EthernetUDP Udp; void setup() { Serial.begin(115200); Ethernet.begin(mac,ip); Udp.begin(localPort); } void loop() { int x = analogRead(A5); delay(2); int y = analogRead(A4); delay(2); int z = analogRead(A3); dati = String(x)+","+String(y)+","+String(z); Serial.println(dati); dati.toCharArray(charBuf, 1024); Udp.beginPacket(remoteIP, remotePort); Udp.write(charBuf); Udp.endPacket(); delay(2); }
----------------------------------------------------
Questa e' una piccola modifica al sistema di invio dati di SismoArduino
In alcuni casi puo' essere comodo non conoscere a priori l'indirizzo del server a cui inviare i dati ed essere comunque in grado di configurare la scheda Arduino
La soluzione e' quella di inviare i pacchetti UDP in modalita' broadcast. In questo modo tutte le macchine della rete ricevono il pacchetto; cio' genera ovviamente un traffico inutile sulla rete ma e' piu' o meno quello che fanno i computer Windows e nessuno si e' mai lamentato piu' di tanto per cui non mi farei problemi
Fonte Wikipedia
Lo sketch di invio deve solo modificare l'indirizzo del server con l'IP di broadcast che e' 255.255.255.255 (la versione 192.168.1.255 non funziona!!)
------------------------------------------- #include <SPI.h> #include <Ethernet.h> #include <EthernetUdp.h> //Arduino byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; IPAddress ip(192,168,1,2); unsigned int localPort = 8888; //Server byte remoteIP[4] = {255,255,255,255}; int remotePort = 5005; String dati; char charBuf[1024]; EthernetUDP Udp; void setup() { Serial.begin(115200); Ethernet.begin(mac,ip); Udp.begin(localPort); } void loop() { int x = analogRead(A5); delay(2); int y = analogRead(A4); delay(2); int z = analogRead(A3); dati = String(x)+","+String(y)+","+String(z); Serial.println(dati); dati.toCharArray(charBuf, 1024); Udp.beginPacket(remoteIP, remotePort); Udp.write(charBuf); Udp.endPacket(); delay(2); }
-------------------------------------------
il programma in Python che funziona da server deve essere invece riscritto per non ignorare i pacchetti broadcast
Dopo i recenti terremoti vicino a Firenze mi e' tornata la voglia di lavorare ad un progetto che avevo in mente da tempo e che con somma fantasia ho denominato SismoArduino. Si tratta di un progetto da un giorno ovvero che non risolve in 10 minuti collegando 4 fili ma che non necessita comunque troppo impegno
Per questo progettino ho tirato fuori l'Arduino Uno (la scelta e' stata obbligata perche' Arduino Due non entra fisicamente nella scatola da esterni in cui alla fine deve essere alloggiato il tutto per metterlo in produzione), uno shield Ethernet originale Arduino e un accelerometro triassiale ADXL335
Per comodita' ho piegato il pin ST in modo da fare contatto ed ho poi inserito i pin Z,Y e X nelle porte A3,A4 ed A5 patchando poi al volo i cavi di alimentazione (collegati a 3.3V e GND)
Lo sketch montato su Arduino semplicemente legge i dati sulle porte analogiche e poi invia una stringa mediante UDP client ad un server
(Arduino e' 192.168.1.177 mentre il server e' 192.168.1.120 in ascolto su porta 5005)
----------------------------------------------------------------------- #include <SPI.h> #include <Ethernet.h> #include <EthernetUdp.h> //Arduino byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; IPAddress ip(192, 168, 1, 177); unsigned int localPort = 8888; //Server byte remoteIP[4] = {192, 168, 1, 120}; int remotePort = 5005; String dati; char charBuf[1024]; EthernetUDP Udp; void setup() { Serial.begin(9600); Ethernet.begin(mac,ip); Udp.begin(localPort); } void loop() { int x = analogRead(A5); delay(2); int y = analogRead(A4); delay(2); int z = analogRead(A3); delay(2); dati = String(x)+","+String(y)+","+String(z); Serial.println(dati); dati.toCharArray(charBuf, 1024); Udp.beginPacket(remoteIP, remotePort); Udp.write(charBuf); Udp.endPacket(); delay(10); }
-----------------------------------------------------------------------
Sul server e' stato messo uno script Python che riceve i dati con un server UDP e li scrive su un database Mysql
-----------------------------------------------------------------------
#!/usr/bin/python
import socket import MySQLdb as mdb import time current_milli_time = lambda: int(round(time.time()*1000)) con = mdb.connect('localhost','root','chiara','sismoarduino') sock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) sock.bind(("192.168.1.120",5005)) while True: data,addr=sock.recvfrom(1024) with con: cur = con.cursor() cur.execute("INSERT INTO sisard (x1,y1,z1,milli,tempo) VALUES ("+data+","+str(current_milli_time())+",now())") print data
Per la tabella Mysql c'e' un piccolo trucco. Mysql non accetta in modo semplice il formato tempo con i millisecondi. Per cui e' stato creato un campo milli come BIGINT dove e' registrato il tempo completo con i millisecondi ed un campo tempo come DATETIME per il lettore umano
----------------------------------------------------------------------- CREATE TABLE `sisard` ( `x1` int(11) DEFAULT NULL, `y1` int(11) DEFAULT NULL, `z1` int(11) DEFAULT NULL, `milli` bigint(20) DEFAULT NULL, `tempo` datetime NOT NULL ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
------------------------------------------------------------------------
La presentazione dei dati e' effettuata in tempo reale mediante un lettore fatto con le librerie Dygraph
I dati per la presentazione a video vengono generati dal seguente scritp php (in pratica viene generato al volo un CSV che viene dato in pasto a DyGraph
------------------------------------------------------------------------
E questa e' la pagina html che ospita Dygraph con aggiornamento automatico ogni secondo (la finestra e' degli ultimi 1000 dati
------------------------------------------------------------------------
il passo di campionamento misurato e' stato di circa 15-16 campioni al secondo (15 Hz) che non sono assolutamente paragonabili a strumenti professionali (i sismografi reali hanno passi di campionamento che vanno da 50 a 100 Hz)
Qui il video della prova
L'aspetto finale e' da sottolineare e' che il server (di recupero) impiegato e' stato un P3 a 700 Mhz con 128 Mb di ram con Debian Wheezy, Apache, Php, Mysql e Xwindow in funzione ed ha comunque funzionato dignitosamente
Il progetto Force Guage sta arrivando al suo termine
L'hardware finale e' stato montato nella sua versione finale
-Arduino Due
-Ethernet Shield
-Amplifier Shield (autoprodotto)
adesso si passa al codice
per Arduino e' stato riutilizzato quasi tutto il codice di questo post
C'e' da annotare che (evindenziato in giallo)
1) per usare il comando itoa in Arduino Due si deve aggiungere #include "itoa.h"
2) per far funzionare il convertitore analogico digitale di Arduino due a 12 bit lo si deve richiedere esplicitamente con il comando analogReadResolution
Codice Arduino
------------------------------------------ #include <SPI.h> // needed for Arduino versions later than 0018 #include <Ethernet.h> #include "itoa.h" byte ardmac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; IPAddress ardip(192, 168, 0, 2); byte Server[] = { 192,168,0,1 }; unsigned int porta = 7755; unsigned int localPort = 8888; // local port to listen on int sensorPin = A3; int sensorValue = 0; EthernetUDP Udp; char buf[12]; // "-2147483648\0" void setup() { Ethernet.begin(ardmac,ardip); Udp.begin(localPort); analogReadResolution(12); } void loop() { sensorValue = analogRead(sensorPin); Udp.beginPacket(Server, porta); Udp.write(itoa(sensorValue, buf, 10)); Udp.endPacket(); delay(100); }
----------------------------------------
per il frontend di rappresentazione realtime dei dati e' stato scritto un programmino in Qt che implementa un server UDP e mostra i dati mediante la libreria QCustomplot
Codice Qt (4.8.5)
file .pro (le modifiche sono in giallo)
------------------------------------------
QT += core gui network
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = geolab
TEMPLATE = app
SOURCES += main.cpp\
mainwindow.cpp \
qcustomplot.cpp
HEADERS += mainwindow.h \
qcustomplot.h
FORMS += mainwindow.ui
------------------------------------------
in azzurro le righe di codice relative al server UDP mentre in giallo quelle relative alla visualizzazione del grafico
nella posizione di riposo (ovvero con la molla a riposo) e con alimentazione a 9 V il segnale e' di circa 780 mV, via via che la testa rientra nel corpo cilindrico azzurro la tensione scende fino a 0 mV
La corsa della testa va da 35 mm (corrispondente a 780 mV) fino a 22. mm (0 mV), per valori di accorciamento superiori il valore e' sempre 0 mV. Si ha quindi una corsa utile di circa 13 mm
Per automatizzare la lettura e permettere la lettura in remoto (all'esterno del laboratorio di geotecnica) la sonda e' stata collegata ad un ingresso analogico della Arduino.
Il valore letto a riposo e' 165 per scendere a 0 a fine corsa. Considerando che la risoluzione di una unita' del convertitore ADC dell'Arduino coincide con circa 4.8 mV si ha una buona corrispondenza con i 780 mV misurati con il tester.
Inoltre visto che la corsa e' di circa 13 mm ed i livelli risultano essere 165 la precisione di campionamento dell'Arduino dovrebbe essere corrispondete al decimo di millimetro
Per permettere la lettura in remoto dei dati la scheda Arduino e' stata programmata come un client UDP che invia le letture ad un server UDP scritto in Python su una Debian Box
La rete e' cosi' configurata
Server : 192.168.0.1
Porta UDP : 5555
Arduino : 192.168.0.2
Lo sketch Arduino utilizzato e' il seguente
----------------------------------------------- #include <SPI.h> // needed for Arduino versions later than 0018 #include <Ethernet.h> byte ardmac[] = {0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED }; IPAddress ardip(192, 168, 0, 2); byte Server[] = { 192,168,0,1 }; unsigned int porta = 5555; unsigned int localPort = 8888; // local port to listen on int sensorPin = A0; int sensorValue = 0; EthernetUDP Udp; char buf[12]; // "-2147483648\0" void setup() { Ethernet.begin(ardmac,ardip); Udp.begin(localPort); } void loop() { sensorValue = analogRead(sensorPin); Udp.beginPacket(Server, porta); Udp.write(itoa(sensorValue, buf, 10)); Udp.endPacket(); delay(1000); }
-----------------------------------------------
mentre il server UDP (molto stupido e da implementare con il salvataggio dei dati)
----------------------------------------------- import socket UDPSock = socket.socket(socket.AF_INET,socket.SOCK_DGRAM) listen_addr = ("",5555) UDPSock.bind(listen_addr) contatore = 0 while True: data,addr = UDPSock.recvfrom(1024) print data.strip(),contatore contatore = contatore + 1
-----------------------------------------------
modificando leggermente lo sketch e' possibile utilizzare una Arduino Uno per monitorare fino a 5 sensori
Un metodo rapido per aprire il firewall iptables di Linux per permettere le connessioni UDP sulla porta 55000 iptables -A INPUT -p udp --destination-port 55000 -j ACCEPT
Per fare delle prove di server UDP puo' essere utile il programma netcat (che non e' installato di default in Debian ma deve essere aggiunto con apt-get install netcat)
una volta avviato il server UDP si puo' inviare pacchetti UDP con la seguente sintassu
dove localhost e' l'indirizzo del server (in questo caso in locale) e 21567 e' la porta di ascolto del server
In questo post viene descritta la possibilita' di stabilire una connessione UDP tra il telefono Android in funzione Server ed un PC mediante script Python
per il lato server (Android) si deve creare un programma che inserisca tra i permessi di AndroidManifest.xml la seguente riga
sulla funzione OnCreate si puo' creare il server UDP come riportato nell'esempio seguente (porta di connessione 12345 e lunghezza massima del pacchetto 1024 bytes