Indice |
Partenza e arrivo
Lavoro per una ditta che produce sensori che vengono utilizzati (tra l'altro) nell'industria bio-farmaceutica: produzione di vaccini, produzione di medicamenti, produzione a scopo di ricerca di virus e batteri.
In un ambito del genere è essenziale poter sterilizzare il sensore, in modo da non contaminare i prodotti quando si cambia processo. Una volta i sensori li si bollivano. Con il passare degli anni le esigenze in fatto di igiene sono diventate sempre piú severe. Oggi la sterilizzazione consiste in un bagno di vapore a 140 °C e 3.7 bar di sovrapressione durante mezz'ora.
Ovviamente l'elettronica non gradisce questo tipo di condizioni ambientali, quindi il sensore deve essere ermetico (e vi garantisco che non è facile!). Per testare la tenuta delle giunture ho costruito delle piccole schedine che montano un sensore di umidità e ho scritto un po' di software per leggere questi sensori.
In questo articolo presento il lavoro svolto e l'interfaccia usata, che credo possa tornare utile a molti hobbysti (o professionisti).
I componenti del sistema
Qui elenco brevemente i componenti che costituiscono il sistema. Nei capitoli seguenti scendo nei dettagli di ogni componente.
- Sensore di umidità
- il sensore scelto è il Si7020 della Silabs
- Interfaccia
- il collegamento con il computer avviene tramite un Bus Pirate
- Software
- l'interfaccia utente è programmata in Python
Silabs Si7020
Ho scelto questo sensore per una serie di motivi:
- temperatura compensata internamente
- uscita digitale
- piccolo (ho problemi di spazio)
- costa poco
- disponibilità presso i grossi distributori
- resiste al trattamento brutale a cui lo sottopongo (temperature di stoccaggio fino a 150 °C)
Il circuito è preso pari pari dal datasheet. Non c'è molto da dire... Qui il layout del circuitino:
Ce l'ho fatto stare in 19 x 5 mm, monofaccia. Il rovescio della medaglia è che devo fare un ponticello tra il pad Vdd di destra e quello di sinistra. Nulla di tragico!
Visto che non c'è molto da dire, riempio la pagina caricando un paio di foto ;-)
Qui si vede un dettaglio del chip, con lo strato capacitivo. Si noti la notevole maestria nel saldare i componenti in modo che sembrino attaccati con lo sputo :-/
Bus Pirate
In realtà tutto l'articolo è una scusa per parlare di questo giocattolo ;-) Ho notato che non se ne parla nel forum, ne deduco che probabilmente non è conosciuto alla maggiorparte dei parteciparti. E allora, signori e signore, eccolo qui, il Pirata dei Bus:
È una schedina grande quanto una carta di credito, con un collegamento USB e un connettore a 10 pin. La porta USB viene vista come UART (Virtual Com Port). Quando ci si collega via terminal il pirata offre un menu testuale per la gestione degli I/O. Questi sono configurabili (sorpresa sorpresa, chi l'avrebbe mai detto) con diversi protocolli di bus: I²C, SPI, 1-Wire,...
Questo aggeggino è utilissimo per lo sviluppo e il debugging. Se il vostro circuito accetta 5 V o 3.3 V e non richiede piú di 300 mA potete alimentarlo direttamente dal pirata. Ah, c'è anche un ingresso ADC, cosa volete di piú? Ciliegina sulla torta, costa 30 miseri dollari. Prima di darvi il link alla pagina del progetto, le canoniche 5 paroline: non-ho-percentuali-sulle-vendite ;-)
http://dangerousprototypes.com/docs/Bus_Pirate
Non è tutto oro quello che luccica: l'input/output avviene, come detto, via terminale. La velocità e 115 kbps, ma c'è tutto l'overhead legato al menu e alla gestione del protocollo. Questo limita la velocità sul lato bus. Inoltre il buffer è limitato, quindi benché il bus possa accettare dati a velocità piú sostenute, l'overflow è sempre in agguato.
Per mitigare questo problema c'è la possibilità di "spegnere" l'interfaccia sul terminal e usare il pirata in modo binario. Piú dettagli in merito nella sezione relativa al software, in cui useremo questo metodo per comunicare con il sensore.
Potrei spiegarvi vita e miracoli dell'utilizzo di questo oggetto, ma visto che online si trova la documentazione relativa, vi risparmio e mi limito ad uno screenshot e due parole:
Qui ho configurato il Bus Pirate in modalità SPI (è estremamente intuitivo, il menu chiede per esempio che velocità si vuole ed elenca diverse frequenze, si dà il numero corrispondente alla frequenza desiderata e si conferma con Enter, il menu passa al punto seguente, la polarità del clock, e così via...).
Inserendo "?" e dando Invio appare il menu che vedete. La colonna di sinistra è sempre uguale e serve a gestire le funzioni generali del pirata e a cambiare modo. La colonna di destra invece contiene i comandi specifici per il protocollo attivo.
Interessante è il comando v: elenca tutti i pin e dice a cosa corrispondono in questo specifico protocollo (nel caso dell'SPI avremo MOSI, MISO, CLK e CS, oltre a tutta la paccottiglia ausiliaria: GND, V3.3, V5, Pull-up, ADC...) assieme al loro stato (1, 0, Hi-Z,...). Come detto, se vi ho incuriosito, spulciate la documentazione ;-)
Software
Bam! Partiamo direttamente con il codice:
import sys from PyQt4 import QtGui, QtCore, uic from PyQt4.QtCore import QObject from pyBusPirateLite.I2Chigh import * class MainWindow(QtGui.QMainWindow,QObject): def __init__(self): super(MainWindow, self).__init__() # averaging: self.avg_n = 3 self.initUI() def initUI(self): self.ui = uic.loadUi('GUI.ui') self.ui.show() #connectors self.connect(self.ui.v_start, QtCore.SIGNAL("clicked()"), self.startClick) def startClick(self): self.ui.v_rh.setMaxLength(15) self.ui.v_t.setMaxLength(15) self.ui.v_rh.setText('reading...') self.ui.v_t.setText('reading...') self.ui.repaint() i2c = I2Chigh("COM"+str( self.ui.v_com.value()), 115200, 5) i2c.BBmode() i2c.enter_I2C() i2c.cfg_pins(I2CPins.POWER) i2c.set_speed(I2CSpeed._400KHZ) rh_sum = 0 for i in range(0, self.avg_n): rh_sum = rh_sum + float(i2c.get_word(0x40, 0xE5)) * 125 / 65536 - 6 rh = rh_sum / self.avg_n self.ui.v_rh.setText(str(rh)) self.ui.v_rh.setMaxLength(5) self.ui.repaint() t_sum = 0 for i in range(0, self.avg_n): t_sum = t_sum + float(i2c.get_word(0x40, 0xE3)) * 175.72 / 65536 - 46.85 t = t_sum / self.avg_n self.ui.v_t.setText(str(t)) self.ui.v_t.setMaxLength(5) i2c.resetBP() return if __name__ == '__main__': app = QtGui.QApplication(sys.argv) mainwindow = MainWindow() sys.exit(app.exec_())
I piú attenti avranno notato che si tratta di Python.
Piccolo excursus. Io odio Python. L'ho già detto qui: http://www.electroyou.it/forum/viewtopic.php?f=16&t=52459&p=506696 e la mia opinione è stata onorata con due punti negativi. I punti negativi non sono riusciti a farmi cambiare opinione. Ci sono i linguaggi di programmazione (C++, per esempio) e ci sono i languaggi di scripting (Python). Purtroppo la libreria per il Bus Pirate c'è solo per Python. È quella che importo all'inizio e si chiama pyBusPirateLite.
Descrivo brevemente il semplicissimo codice. Nel constructor dichiaro la variabile avg_n che definisce quanti samples vengono presi per fare la media del valore che verrà poi visualizzato. Dopodiché inizializzo l'interfaccia grafica. Questa viene caricata dal file GUI.ui (vedo con Admin se posso caricarlo, altrimenti, chi vuole averlo può mandarmi un messaggio... oppure come compito scriverlo in base a quanto si vede nel codice qui sopra ;-)). Nell'interfaccia grafica c'è un bottone "Start". Questo viene collegato alla funzione startClick. E qui il programma si ferma e aspetta che io clicchi il magico tasto.
Quando faccio click, parte la funzione startClick. Eccone il contenuto, punto per punto:
- setto la massima lunghezza dei campi umidità e temperatura a 15 in modo da poterci scrivere "reading..."
- setto il testo in ambo i campi a "reading..."
- aggiorno la GUI in modo che il cambiamento venga visualizzato
- creo un'instanza i2c della classe I2Chigh. La porta COM su cui è attaccato il pirata me la leggo dalla GUI
- entro in modo binario
- entro in modalità I2C
- abilito i regolatori di tensione per il mio circuito
- setto la velocità del bus
Fin qui tutto molto semplice. Niente paura, anche quello che segue è semplice ;-)
Ho due sezioni simili, una per la temperatura e l'altra per l'umidità. Sono costituite da un loop-for della lunghezza del numero di samples che abbiamo definito prima. Ad ogni ripetizione del loop aggiungo ad una somma che ho inizializzato con zero il valore letto. Questo si ricava con la formula contenuta nel datasheet del Si7020 partendo dal codice restituito sul bus. Come si ottiene il codice? Il cuore di tutta la faccenda è quel float(i2c.get_word(0x40, 0xE5)).
È un comando dato sulla classe i2c che abbiamo instanziato prima. Il comando è get_word, ovvero leggi un word dal bus. i Parametri sono 0x40, l'indirizzo I2C del sensore, e 0xE5, il registro in cui si trova il valore di umidità. Il cast su float è necessario perché altrimenti il codice verrebbe interpretato come un int e la divisione non darebbe il risultato aspettato.
Finito il loop, divido per il numero di addendi per ottenere la media, la scrivo nel campo della GUI (dopo averla convertita in una stringa), setto la lunghezza massima del campo a 5 (non ha molto senso avere 12 cifre significative su un sensore con il 3% di precisione...).
L'ultimo comando resetta il pirata, spegnendo l'alimentazione e uscendo dalla modalità I2C.
Il risultato
Ecco uno screenshot del programmino
Ed ecco il setup di misura, si noti la demoboard di Sensirion con il SHT11, lo scostamento dei valori misurati è ampiamente entro le tolleranze:
Spero che questo progettino vi possa tornare utile o, perlomeno, abbia suscitato un po' di interesse per il pirata ;-)