Netduino, BMP085 Driver in VB .NET
Il Sensore BMP085
Il sensore Bosch BMP085 è un sensore di temperatura e pressione barometrica ad alta precisione. Il sensore è basato sulla tecnologia piezo-resistiva che fornisce alla scheda grande accuratezza, robustezza e stabilità a lungo termine. .Nella modalià ad alta risoluzione il sensore di pressione raggiunge un’accuratezza di 0.003hPa in un range compreso tra i 300 e i 1100hPa. Il sensore viene fornito già calibrato dal produttore con i coefficienti di calibrazione precaricati nella ROM. Naturalmente è possibile modificare tali coefficienti andando a scrivere in alcune locazioni dimemoria interna tramite il bus I2C.

Normalmente viene fornito premontato in una mini-scheda fornita di piazzole prestagnate poste a distanza standard (passo 2,54mm). In questo modo si rende il sensore di pressione barometrica facilmente integrabile in sistemi preesistenti tramite l’utilizzo di connettore strip maschio facilmente utilizzabile nelle comunissime breadboard, millefori o schede di prototipazione. Normalmente le schede sono alimentate con una tensione di alimentazione tra i 3.0V e i 5.0V, anche se il sensore viene alimentato con una tensione regolata a 3.3V. Prima di alimentare la scheda, leggete sempre le istruzioni di utilizzo e state attenti a come la collegate al micro per non incorrere in spiacevoli conseguenze.
Come accennato precedentemente, la scheda si interfaccia con l’esterno tramite il bus I2C. Si ricorda che per far funzionare correttamente il bus I2C, i segnali SDA ed SCL devono essere collegati ad opportune resistenze di pull-up e che spesso queste sono già integrate sulle schede dei microcontrollori.
La matematica del sensore
Nel manuale del sensore si trova la lista delle operazioni da fare per calcolare il valore di temperatura e pressione reale dai dati grezzi provenienti dal sensore utilizzando i parametri di compensazione memorizzati all’interno. Infatti, i dati “grezzi” letti dal sensore sono dati “non compensati” cioè valori che non tengono conto delle derive termiche e del fatto che anche se i sensori sono costruiti in serie, questi comunque si comportano in modo leggermente diverso tra loro. Secondo il manuale le operazioni (in pseudocodice) da eseguire sono:
...
'Resolution Preset (0,1,2,3)
' 0 = Ultra Low Power
' 1 = Standard
' 2 = High Resolution
' 3 = Ultra High Resolution
OSS = 1
'Read registers
READ (0xAA, 0xAB) -> AC1
READ (0xAC, 0xAD) -> AC2
READ (0xAE, 0xAF) -> AC3
READ (0xB0, 0xB1) -> AC4
READ (0xB2, 0xB3) -> AC5
READ (0xB4, 0xB5) -> AC6
READ (0xB6, 0xB7) -> B1
READ (0xB8, 0xB9) -> B2
READ (0xBA, 0xBB) -> MB
READ (0xBC, 0xBD) -> MC
READ (0xBE, 0xBF) -> MD
'Read uncompensated Temperature
WRITE 0x2E -> 0xF4
WAIT 4.5 ms
READ (0xF6, 0xF7) -> UT
'Read uncompensated Pressure
WRITE 0x34 + (OSS << 6) -> 0xF4
SELECT CASE OSS
CASE 0: WAIT 4.5ms
CASE 1: WAIT 7.5ms
CASE 2: WAIT 13.5ms
CASE 3: WAIT 25.5ms
END SELECT
READ (0xF6, 0xF7, 0xF8) >> (8-OSS) -> UP
'Calculate Temperature ( 0.1 Celsius )
X1 = (UT -AC6) * (AC5 / 2^15)
X2 = MC * 2^ 11 / (X1 + MD)
B5 = X1 + X2
T = (B5 + 8) / 2^4
'Calculate Pressure ( Pascal )
B6 = B5 - 4000
X1 = (B2 * (B6 * B6 / 2^12)) / 2^11
X2 = AC2 * B6 / 2^11
X3 = X1 + X2
B3 = ((AC1 * 4 + X3) << OSS + 2) / 4
X1 = AC3 * B6 / 2^13
X2 = (B1 * (B6 * B6 / 2^12)) / 2^16
X3 = ((X1 + X2) + 2) / 2^2
B4 = AC4 * (unsigned long)(X3 + 32768) / 2^15
B7 = ((unsigned long)UP - B3)*(50000 >> OSS)
IF (B7 < 0x80000000) THEN
P = (B7 * 2) / B4
ELSE
P = (B7 / B4) * 2
END
X1 = (P / 2^8) * (P / 2^8)
X1 = (X1 * 3038) / 2^16
X2 = (-7357 * P) / 2^16
P = P + (X1 + X2 + 3791) / 2^4
...
Tutti i calcoli nello script sono eseguiti utilizzando numeri interi (signed o unsigned) a diversa risoluzione (a 16, 32 o 64 bit). Anche se da un punto
di vista della precisione la cosa non avrebbe un gran chè senso, ho riscritto l’algoritmo utilizzando tutte operazioni in virgola mobile e, dove si
poteva, raggruppando le costanti.Utilizzando le variabili in virgola mobile, anche se il numero di calcoli sono diminuiti lievemente, ora ed è
possibile passare comodomante da un’unità di misura ad un’altra senza perdere risoluzione.
La classe BMP085
Passiamo ora alla stesura della classe BMP085 per la lettura della pressione e temperatura. Prima di tutto si eredita la classe I2CPlug (vedere il relativo articolo) e successivamente vengono definite alcune costanti per indirizzare due registri del sensore e il bit 7 che corrisponde al valore decimale 128 oppure
&H80 in esadecimale (nel byte i bit vengono indicizzati da 0 a 7).
...
Public Class BMP085
Inherits I2CPlug
'Internal Single Values
Private c4 As Single
Private c5 As Single
Private c6 As Single
Private b1 As Single
Private mb As Single
Private mc As Single
Private md As Single
Private x0 As Single
Private x1 As Single
Private x2 As Single
Private y0 As Single
Private y1 As Single
Private y2 As Single
Private p0 As Single
Private p1 As Single
Private p2 As Single
Private Buffer1 As Byte() = New Byte(0) {}
Private Buffer2 As Byte() = New Byte(1) {}
Private Buffer3 As Byte() = New Byte(2) {}
....
''' Sensor resolution
Public Property Resolution() As Resolutions
Get
Return m_Resolution
End Get
Set(ByVal value As Resolutions)
m_Resolution = value
End Set
End Property
...
Public Sub New(ByVal Address As UShort, Optional ByVal ClockRateKHz As Integer = 100, Optional ByVal TimeOut As Integer = 100)
'Device creation
MyBase.New(Address, ClockRateKHz, TimeOut)
End Sub
Public Sub Calibrate()
'Variable declaration
Dim Buffer() As Byte = New Byte(21) {}
'Read the internal BOSH Registers
MyBase.ReadRegister(&HAA, Buffer)
'Basic Single Coefficients Calculation
c4 = Me.ToSingle(Buffer(6), Buffer(7), True) * 0.00000003051758F
c5 = Me.ToSingle(Buffer(8), Buffer(9), True) * 0.000000190734866F
c6 = Me.ToSingle(Buffer(10), Buffer(11), True)
mb = Me.ToSingle(Buffer(16), Buffer(17))
b1 = Me.ToSingle(Buffer(12), Buffer(13)) * 0.0000238418579F
mc = Me.ToSingle(Buffer(18), Buffer(19)) * 0.08F '/ 160 / 160 * 2048
md = Me.ToSingle(Buffer(20), Buffer(21)) * 0.00625F
'Derived Polinomial Values
x0 = Me.ToSingle(Buffer(0), Buffer(1)) '* 1
x1 = Me.ToSingle(Buffer(2), Buffer(3)) * 0.01953125F '* 160 / 8192
x2 = Me.ToSingle(Buffer(14), Buffer(15)) * 0.000762939453F '* 160 * 160 / 33554432
y0 = c4 * 32768
y1 = c4 * Me.ToSingle(Buffer(4), Buffer(5)) * 0.0048828125F '* 160 / 32768
y2 = c4 * Me.ToSingle(Buffer(12), Buffer(13)) * 0.0000238418579F '* 160 * 160 / 1073741824
p0 = 236.4375F '= (3791 - 8) / 1600 * 100
p1 = 99.2983856F '= 1 - 7357 / 1048576 * 100
p2 = 0.000442087185F '= 303800 / 68719476736 * 100
'Offset Recalculation
m_Offset = 0
Me.Calculate()
m_Offset = m_Altitude - Altimetry
'Memory cleaning
Erase Buffer
Buffer = Nothing
End Sub
Private Sub Calculate()
Dim Tmp As Single = 0
Dim Prs As Single = 0
Dim Alt As Single = 0
Dim aph As Single = 0
Dim s1 As Single = 0
Dim s2 As Single = 0
Dim z As Single = 0
'Select the temperature registry
MyBase.Write(New Byte() {&HF4, &H2E})
'Maximum Conversion Time for temperature
Thread.Sleep(5)
'Read the temperature
MyBase.ReadRegister(&HF6, Buffer2)
'TEMPERATURE CALCULATION
Tmp = Me.ToSingle(Buffer2(0), Buffer2(1), CByte(0))
aph = c5 * (Tmp - c6)
Tmp = aph + mc / (aph + md)
'Temperature Output (Celsius)
m_Temperature = Tmp
'Select the pressure registry and correct conversion time
Select Case m_Resolution
Case Resolutions.Low
MyBase.WriteRegister(&HF4, New Byte() {CType(&H34, Byte)})
Thread.Sleep(5)
Case Resolutions.Standard
MyBase.WriteRegister(&HF4, New Byte() {CType(&H74, Byte)})
Thread.Sleep(8)
Case Resolutions.High
MyBase.WriteRegister(&HF4, New Byte() {CType(&HB4, Byte)})
Thread.Sleep(14)
Case Resolutions.Ultra
MyBase.WriteRegister(&HF4, New Byte() {CType(&HF4, Byte)})
Thread.Sleep(26)
End Select
'Preset the Pressure Registry
MyBase.ReadRegister(&HF6, Buffer3)
'Uncompensated Pressure reading
Prs = Me.ToSingle(Buffer3(0), Buffer3(1), Buffer3(2))
'PRESSURE CALCULATION
s1 = Tmp - 25
s2 = s1 * s1
z = (Prs - (x2 * s2 + x1 * s1 + x0)) / (y2 * s2 + y1 * s1 + y0)
Prs = (p2 * z * z + p1 * z + p0)
'Pressure Output (Pascal)
m_Pressure = Prs
End Sub
...
End Class
...
Viene ridefinito il metodo New() per la creazione della classe, ma solo per cambiare il valore di default del ClockRate per renderlo piú congeniale al dispositivo. I due metodi principali della classe sono Calibrate() e Calculate(). Con Calibrate() si leggono i coefficienti interni del sensore (c4, c5, c6, mb, mc, md e b1) trasformandoli in valori floating point (single). Poi si leggono e si calcolano i coefficienti dei tre polinomi di secondo grado che servono per calcolare la pressione compensando le variazioni dovute alla temperatura ( x0, x1, x2, y0,y1, y2 e p0, p1, p2).
Il metodo Calculate() legge il valore di temperatura e pressione (non compensata) e poi utilizzato i coefficienti pre-calcolati e memorizzati con il metodo precedente, determina il valore di temperatura, di pressione e altitudine. La legge di compensazione è data dalla seguente formula :

dove con la variabile Pc si è indicata la pressione compensata mentre con la variabile z un valore dipendente dalla temperatura e dalla pressione non compensata. Questa variabile si ottiene utilizzando le due formule:

dove la temperatura tu è quella effettiva mentre t è quella relativa alla temperatura standard di 25 °C.
Nel metodo Calculate() viene posto in Sleep il thread per un numero di millisecondi dipendente dalla risoluzione desiderata memorizzata nella variabile interna m_Resolution. Questa variabile è anche una proprietè della classe e può avere solo quattro valori: Low, Standard, High e Ultra. In generale, più $egrave; alta la risoluzione e più tempo ci vuole per la conversione.
La temperatura e la pressione sono espresse rispettivamente in gradi Celsius e Pascal. Per la lettura dei valori la classe ha le seguenti proprietà
ReadOnly:
...
''' Temperature
Public ReadOnly Property Temperature(Optional ByVal Unit As TemperatureUnit = TemperatureUnit.Celsius) As Single
Get
Select Case Unit
Case TemperatureUnit.Celsius
Return m_Temperature
Case TemperatureUnit.Kelvin
Return (m_Temperature + 273.15F)
Case TemperatureUnit.Fahrenheit
Return (m_Temperature * 1.8F + 32.0F)
Case TemperatureUnit.Rankine
Return (m_Temperature * 1.8F + 491.67F)
Case Else
Return 0
End Select
End Get
End Property
''' Pressure
Public ReadOnly Property Pressure(Optional ByVal Unit As PressureUnit = PressureUnit.Millibar) As Single
Get
Select Case Unit
Case PressureUnit.Pascal
Return m_Pressure
Case PressureUnit.Bar
Return m_Pressure / 100000.0F
Case PressureUnit.Atmosphere
Return m_Pressure / 1101325.0F
Case PressureUnit.Torr
Return m_Pressure / 133.322F
Case PressureUnit.Psi
Return m_Pressure / 6895.0F
Case PressureUnit.Millibar
Return m_Pressure / 100
Case Else
Return 0
End Select
End Get
End Property
...
Queste due proprietà permettono di trasformare gli ultimi valori letti nell’unità di misura desiderata. Per le temperature abbiamo quattro scelte (Celsius, Kelvin, Fahrenheit, Rankine) mentre per le pressioni sei (Pascal, Bar, Atmosphere, Torr, Psi e Millibar). Vediamo ora come utilizzare la classe.
Utilizzo della Classe
Prima di tutto bisogna creare la classe BMP085 passando nel New() l’indirizzo del BUS I2C &H77, inserendola nella chiamata Sub Main e poi
eseguire una calibrazione del sensore chiamando il metodo Calibrate(). Se si desidera leggere i valori di Pressione e Temperaura in tempo Reale, bisogna chiamare il metodo Calculate() prima dell’utilizzo delle variabili in modo da aggiornare e ricalcolare i valori interni.
Public Sub Main() Dim BAR As BMP085 = New BMP085(&H77) Dim Temperature as Single = 0 Dim Pressure as Single = 0 'Calibrazione BAR.Calibrate 'Esegui per sempre ... While True 'Lettura dei dati BAR.Calculate ... 'Lettura della Temperatura Temperature = BAR.Temperature(TemperatureUnit.Celsius) 'Lettura della Pressione Pressure = BAR.Pressure(PressureUnit.Millibar) ... Debug.Print(Temperature.ToString & " Celsius : " & Pressure.ToString & " millibar") ... End While End Sub
Informazioni e Risorse
![]() |
This work is licensed under a Creative Commons Attribution 4.0 International License |
![]() |
BOSH BMP085 Sensor Data Sheet |
![]() |
Download the BMP085 Sources v1.0 (VB.NET) |









