Manual
do
Maker
.
com
BMP180 com Raspberry? Poderia ser apenas mais um artigo após uma longa temporada de sofrimento solitário em busca de um sorriso regado à lágrimas, mas dessa vez tenho o prazer, a honra, o orgulho e o puxa-saquismo de anunciar o primeiro post com ajuda direta do meu chefe, Denis Orsi. Não foi ajuda do tipo "formatar o texto"; foi bem do tipo "formatar a fórmula", sem hipérboles e não-figurativo. Explico no decorrer desse artigo.
Já há algum tempo eu chocava alguns desses barômetros por serem fantásticos; uma miniatura dessa com capacidade de medir a pressão atmosférica e altitude, além de medir com muita precisão a temperatura. Nesse artigo disponho do código Python utilizado no ato do desenvolvimento. "No ato do desenvolvimento" é um sinônimo explícito de "rascunho", primo da discrepância e passivo de incoerências.
Se alguém (ainda que uma pessoa só) pedir para organizar em uma classe, eu escrevo e disponibilizo com prazer, mas por ora é apenas isso.
Como diria o Steeve (amigo do Osmar - a primeira fatia do pão de forma):
"Existem dois tipos de pessoas; as que lêem o datasheet e as que não gostam de patinhas de sirí".
Eu quase sempre lia o datasheet em busca de alguma informação extra, mas desde que comecei a trabalhar com o Denis, a primeira coisa que faço é ler o datasheet. Não adianta pegar material pronto como por exemplo, uma biblioteca; inseri-la, chamar seu método e não saber como um dispositivo funciona. E isso não é o pior; tem tutoriais que induzem à perdição! Vi um tutorial de um dispositivo com nível lógico de 3.3V e o cara dizendo pra chutar o pau em 5V que não tinha problema. Aí olho o datasheet e 'pow'! O ponto de morte estava marcado como 4.5V no pino lógico. Por isso a recomendação: leia-o-datasheet!
Para o BMP180 não haveria outra opção, claro. E veja o porquê:
De forma alguma você faria esse dispositivo funcionar a contento sem ler o datasheet - exceto estivesse esperando por esse artigo, claro.
Se você achou que agora ficou fácil lendo essa fórmula, experimente implementar antes de usar o desse artigo. É o C A O S implementar isso, sério.
Se desejar verificar, os coeficientes de calibração estão na página 13 do datasheetBST-BMP180-DS000-09.pdf.
Basicamente, é necessário fazer uma leitura inicial para calibrar o dispositivo. Essa leitura inicial é armazenada em diversas variáveis e devem ser utilizadas a seu tempo, depois algumas delas são modificadas, mas elas devem ser lidas na ordem que indica a fórmula para não dar muita variação no resultado final.
Existem 3 níveis de precisão para a leitura e cálculo da pressão atmosférica (em hPa) e utilizamos "hard-coded" o nível 3, que é o mais preciso.
Os delays inseridos no código não são enfeites; estão nas posições onde os tempos de delay são necessários e um pouquinho acima do tempo suficiente para o dispositivo "jogar o cabelo pro lado certo".
Normalmente inicio com essas informações - nunca cito todas, apenas as necessárias para uma apresentação. Claro que hoje não será diferente, exceto pelo fato da disposição da informação.
O BMP180 é um sensor digital de pressão criado pela Bosch, com níveis de medição entre 300 e 1100Pa (9000 metros à -500 metros relativo ao nível do mar). É um LGA com dimensões de 4mmX4mm (sem contar a board, obviamente), consome 5uA e trabalha entre as tensões de 1.62V à 3.6V. Ele mede temperatura, pressão e altitude, possui interface I²C e os usos ficam por conta da sua imaginação.
A temperatura tem precisão de 16bits e a pressão tem precisão de 16 à 19bits. É isso.
Pois é, não basta conectar o dispositivo ao Raspberry. Para habilitar o I²C, siga esse tutorial que escrevi como introdutório aos artigos relacionados à I²C, é fundamental que seja feito. Após isso, volte a esse ponto e dê sequência.
Não vou falar de wiring porque é uma vergonha. Leia esse artigo. Todos os dispositivos I²C são conectados assim; SDA, SCL, VCC e GND. Ponto.
O código está no estado mais crú (eu tiraria o acento e o 'R') que se pode imaginar. Está 100% funcional, mas está zoneado. Se for pra criticar, me mande 100 reais primeiro. Se for elogiar, antecipo meu muito obrigado - e creio que falo pelo Denis também nesse aspecto.
#!/usr/bin/env python
from smbus import SMBus
import time
ADDR = 0x77
bus = SMBus(1)
def invertWord(val):
return ((val&0xff)<<8)+(val>>8)
def toSigned(val):
return val if val<0x8000 else val-0x10000
#Leitura inicial de todos os valores pertinentes
def calibrate(addr):
global addresses
addresses = {"AC1":toSigned(invertWord(bus.read_word_data(addr, 0xAA))),
"AC2":toSigned(invertWord(bus.read_word_data(addr,0xAC))),
"AC3":toSigned(invertWord(bus.read_word_data(addr, 0xAE))),
"AC4":toSigned(invertWord(bus.read_word_data(addr, 0xB0))),
"AC5":toSigned(invertWord(bus.read_word_data(addr, 0xB2))),
"AC6":toSigned(invertWord(bus.read_word_data(addr, 0xB4))),
"B1":toSigned(invertWord(bus.read_word_data(addr, 0xB6))),
"B2": toSigned(invertWord(bus.read_word_data(addr, 0xB8))),
"MB":toSigned(invertWord(bus.read_word_data(addr, 0xBA))),
"MC":toSigned(invertWord(bus.read_word_data(addr, 0xBC))),
"MD":toSigned(invertWord(bus.read_word_data(addr, 0XBE)))}
def getTemp(addr):
#leitura discompensada da temperatura
#escreve 0x2E para 0xF4 aguarda 4.5ms
bus.write_byte_data(addr,0xF4,0x2E)
time.sleep(0.010)
#le 0xF6 (MSB) e 0xF7 (LSB)
MSB = bus.read_byte_data(addr,0xF6)
LSB = bus.read_byte_data(addr,0xF7)
UT = (MSB << 8) + LSB
#calcular temperatursa
X1 = float(UT - addresses["AC6"])*addresses["AC5"]/2**15
X2 = addresses["MC"]*2**11/(X1+addresses["MD"])
B5 = X1 + X2
T = round((B5+8)/160,1)
return T
def getTempAndPressure(addr):
# leitura discompensada da temperatura
# escreve 0x2E para 0xF4 aguarda 4.5ms
bus.write_byte_data(addr, 0xF4, 0x2E)
time.sleep(0.010)
# le 0xF6 (MSB) e 0xF7 (LSB)
MSB = bus.read_byte_data(addr, 0xF6)
LSB = bus.read_byte_data(addr, 0xF7)
UT = (MSB << 8) + LSB
#pressao
bus.write_byte_data(addr,0xF4,0xF4)
time.sleep(0.03)
MSB = bus.read_byte_data(addr, 0xF6)
LSB = bus.read_byte_data(addr, 0xF7)
XLSB = bus.read_byte_data(addr, 0xF8)
UP = ((MSB << 16) + (LSB <<8) + XLSB) >> (8-3)
print "UP =", UP
# calcular temperatura
X1 = float(UT - addresses["AC6"]) * addresses["AC5"] / 2 ** 15
X2 = addresses["MC"] * 2 ** 11 / (X1 + addresses["MD"])
B5 = X1 + X2
T = round((B5 + 8) / 160,1)
#calcular pressao
B6 = B5-4000
X1 = (addresses["B2"]*(B6**2/2**12))/2**11
X2 = addresses["AC2"]*B6/2**11
X3 = X1+X2
B3 = ((addresses["AC1"]*4+X3)*2**3)/4
print "B5 =",B5
print "B6 =",B6
print "B2 =",addresses["B2"]
print "X1 =",X1
print "AC2 =",addresses["AC2"]
print "X2 =",X2
print "AC1 =",addresses["AC1"]
print "B3 =",B3
X1 = addresses["AC3"]*B6/2**13
X2 = (addresses["B1"] * (B6**2/2**12))/2**18
X3 = (X1+X2)/4
B4 = addresses["AC4"]*(X3+32768)/2**15
print "AC3 =",addresses["AC3"]
print "X1 =",X1
print "B1 =",addresses["B1"]
print "X2 =",X2
print "X3 =",X3
print "AC4 =",addresses["AC4"]
print "B4 =",B4
B7 = (UP-B3)*(50000 >> 3)
P = B7/B4*2
print "B7 =",B7
print "P =",P
X1 = (int(P)>>8)**2
print "X1 (primeiro) = ",X1
X1 = float(X1*3038)/2**16
X2 = (-7357*P)/2**16
#pressao em hPa (resolucao 0.01)
P = round((P+(X1+X2+3791)/16)/100,2)
print "x1 = ",X1
print "X2 = ",X2
print P
Basta salvar esse código em um arquivo. Se quiser experimentá-lo com bpython, siga esses passos:
import seuArquivoComEsseCodigoSemExtensao as teste
teste.calibrate(0x77)
teste.getTemp(0x77)
teste.getTempAndPressure(0x77)
Fácil ou não? É, utilizar agora ficou fácil, mas dessa vez o trabalho foi feito a quatro mãos, e podem esperar por mais!
Aproveite para ler esse artigo sobre dicas, dúvidas e erros comuns com Raspberry.
Inscreva-se no nosso canal Manual do Maker Brasil no YouTube.
Próximo post a caminho!
Autor do blog "Do bit Ao Byte / Manual do Maker".
Viciado em embarcados desde 2006.
LinuxUser 158.760, desde 1997.