joi, 17 iulie 2014

BeagleBoard - presiuni si temperaturi - senzorul BMP085

Avem un BeagleBoard si dorim sa citim presiunea atmosferica prin intermediul uni senzor de presiune barometrica BMP085. Senzorul este grozav pentru pretul sau, ofera inclusiv un senzor de temperatura (totusi limitat de la 0 la 65 de grade Celsius) si cu ajutorul sau poti inclusiv sa afli altitudinea (presiunea atmosferica variaza cu altitudinea).
Senzorul BMP085 este un senzor digital ce se conecteaza prin I2C (adica el este un dispozitiv ce are o adresa si diversi registri in care scriem si dispozitivul executa o functie sau ofera informatii localizate in alti registri (pe care ii citim)). 
Sa trecem la treaba:
-citim documentatia senzorului si aflam semnificatia pinilor, mai aflam si ca senzorul functioneaza cu niveluri logice intre 3V3 si 5V.

-punem firele necesare la senzor (SDA, SCL, GND, Vcc)


-citim documentatia placii BeagleBoard RevC4 si aflam unde sunt pinii I2C cu care trebuie sa legam senzorul BMP085 (conectorul J3). Pe acest conector avem 5V.
-conectorul J3 nu este fizic pe placa, deci va trebui sa il lipim acolo, am ales un conector header cu pini la 2.54mm la 90 de grade


-legam firele SDA (pin 23), SCL (pin 24), GND (pin 27 sau 28) si 5Vcc (pin 2) de la conectorul J3 de pe placa BeagleBoard.


-acum partea hardware este in buna stare, trebuie sa o folosim: trebuie instalat pachetul i2c tools:
sudo apt-get install i2c-tools
-verificam ce busuri I2C sunt disponibile pe placa - cel cu 2 e pentru expansion header (conectorul J3):


i2cdetect -l

 Rezultatul:
i2c-1   i2c             OMAP I2C adapter                        I2C adapter
i2c-2   i2c             OMAP I2C adapter                        I2C adapter
i2c-3   i2c             OMAP I2C adapter                        I2C adapter

Inseamna ca I2C de pe J3 este disponibil - i2c-2
Acum scanam sa vedem ce dispozitive sunt legate pe acest bus (-y e pt a evita intrebarea Y/N) – daca apare un dispozitiv conectat de noi, il deconectam si mai cautam o data pt a fi siguri ca acela este - in cazul nostru BMP085 are adresa 0x77:

sudo i2cdetect -y -r 2
0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- 77 
 

 -pentru programare si utilizare I2C in Python, trebuie instalat pachetul python-smbus:


sudo apt-get install python-smbus

Documentatia senzorului BMP085 ofera algoritmul de citire a temperaturii cat si al presiunii atmosferice. De asemenea ofera algoritmul de calcul al inaltimii in functie de presiunea atmosferica. Astfel ca se poate incropi cod simplu in C, in Python sau se poate lua codul gata scris de altii si adapta la aplicatie:


#! /usr/bin/env python
import smbus
import time

class BMP085():

    OSS = 3

    def __init__(self, i2c, address):
        self.i2c = i2c
        self.address = address
        self.ac1 = self.readSignedWord(0xaa)
        self.ac2 = self.readSignedWord(0xac)
        self.ac3 = self.readSignedWord(0xae)
        self.ac4 = self.readWord(0xb0)
        self.ac5 = self.readWord(0xb2)
        self.ac6 = self.readWord(0xb4)
        self.b1 = self.readSignedWord(0xb6)
        self.b2 = self.readSignedWord(0xb8)
        self.mb = self.readSignedWord(0xba)
        self.mc = self.readSignedWord(0xbc)
        self.md = self.readSignedWord(0xbe)

    def readWord(self, reg):
        msb = self.i2c.read_byte_data(self.address, reg)
        lsb = self.i2c.read_byte_data(self.address, reg + 1)
        value = (msb << 8 ) + lsb
        return value

    def readSignedWord(self, reg):
        msb = self.i2c.read_byte_data(self.address, reg)
        lsb = self.i2c.read_byte_data(self.address, reg+1)
        if (msb > 127):
            msb = msb - 256
        value = (msb << 8 ) + lsb
        return value

    def readUT(self):
        self.i2c.write_byte_data(self.address, 0xf4, 0x2e)
        time.sleep(0.0045)
        ut = self.readWord(0xf6)
        return ut

    def readTemperature(self):
        ut = self.readUT()
        x1 = ((ut - self.ac6) * self.ac5) >> 15
        x2 = (self.mc << 11) / (x1 + self.md)
        self.b5 = x1 + x2
        return ((self.b5 + 8 ) >> 4) / 10.0

    def readUP(self):
        self.i2c.write_byte_data(self.address, 0xf4, 0x34 + (self.OSS << 6))
        delay = (2 + (3 << self.OSS)) / 1000.0
        time.sleep(delay)
        msb = self.i2c.read_byte_data(self.address, 0xf6)
        lsb = self.i2c.read_byte_data(self.address, 0xf7)
        xlsb = self.i2c.read_byte_data(self.address, 0xf8)
        up = (msb << 16) + (lsb << 8 ) + xlsb
        up = up >> (8 - self.OSS)
        return up



    def readPressure(self):
        up = self.readUP()
        b6 = self.b5 - 4000
       
        x1 = (self.b2 * (b6 * b6)>>12)>>11
        x2 = (self.ac2 * b6)>>11
        x3 = x1 + x2
       
        b3 = (self.ac1 * 4 + x3)<>2
       
        x1 = (self.ac3 * b6) >> 13
        x2 = (self.b1 * ((b6 * b6)>>12))>>16
        x3 = ((x1 + x2) + 2)>>2
       
        b4 = (self.ac4 * (x3 + 32768))>>15
        b7 = (up - b3) * (50000>>self.OSS)
        if (b7 < 0x80000000):
            p = (b7<<1 b4="" br="">        else:
            p = (b7/b4)<<1 br="">       
        x1 = (p>>8) * (p>>8)
        x1 = (x1 * 3038)>>16
        x2 = (-7357 * p)>>16
       
        p += (x1 + x2 + 3791)>>4
       
        return p

i2c = smbus.SMBus(2)
bmp085 = BMP085(i2c, 0x77)
t = bmp085.readTemperature()
p = bmp085.readPressure()

print "Temperature: %.2f C" % t
print "Pressure: %.2f hPa" % (p / 100)


Documentatie i2c tools:


Programarea interfetei  I2C:

Ceva hardware (EEPROM si altele):

Daca folosim niveluri logice diferite, este necesar un level shifter la 3V3 la 5V (de exemplu):