development of a module for the bmp180 pressure sensor
-
- Posts: 288
- Joined: Sun May 04, 2014 8:54 am
development of a module for the bmp180 pressure sensor
[This is not a micropython speficic question, but a general one.]
So, I'm trying to write a module for the BMP180. It's connected via i2c and I figured that out already, I think.
However, I have to read some values from the sensors EEPROM for calibration, and I can't figure out how to get integers from the Bytes. (edit: changed Bits to Bytes)
MSB: \x1b
LSB: \x1b
According to the data sheet, it's a short and it should be somwhere around 400.
Using struct.unpack() gives me results that are not really what I'd expect.
i.E.
>>>struct.unpack('>h', '\x1b\x1b')
(6939,)
>>>struct.unpack('>h', '\x1b')
(6912,)
>>>struct.unpack('>h', '1b')
(12642,)
>>>struct.unpack('>h', '1b1b')
(12642,)
I don't get it. At all. I know hex 1b is 27. Hex 1b1b should be 443. And that the MSB comes first, then the LSB, or the other way round, depending on big/little endian.
So, I'm trying to write a module for the BMP180. It's connected via i2c and I figured that out already, I think.
However, I have to read some values from the sensors EEPROM for calibration, and I can't figure out how to get integers from the Bytes. (edit: changed Bits to Bytes)
MSB: \x1b
LSB: \x1b
According to the data sheet, it's a short and it should be somwhere around 400.
Using struct.unpack() gives me results that are not really what I'd expect.
i.E.
>>>struct.unpack('>h', '\x1b\x1b')
(6939,)
>>>struct.unpack('>h', '\x1b')
(6912,)
>>>struct.unpack('>h', '1b')
(12642,)
>>>struct.unpack('>h', '1b1b')
(12642,)
I don't get it. At all. I know hex 1b is 27. Hex 1b1b should be 443. And that the MSB comes first, then the LSB, or the other way round, depending on big/little endian.
Last edited by Turbinenreiter on Wed Jul 02, 2014 12:53 pm, edited 2 times in total.
Re: I2C, MSB, LSB, endianness and I'm in over my head
0x1b1b = 6939 not 443. 443 = 0x01bb
Lets look at a better example.
<h says to interpret the bytes as little-endian, so the '\x00' will the the LSB and the '\x01' will be the MSB. Thus wee get 0x0100 or 256.
>h says to interpret the bytes as big-endian, to the '\x00' will be the MSB and the '\x01' will be the LSB. Thus we get 0x0001 or 1
443 = 0x01BB not 0x1B1B
And here's how to explain your examples: 0x1b1b is equal to 6939.
Here you've essentally not provided the second byte, so its the same as '\x1b\x00' The greater says big endian. 0x1b00 is 6912
Here, you've provided character constants. '1b' is the same as b'\x31\x62' and 0x3162 = 12642
This just used the first 2 bytes of the 4 byte string you provided so same answer as before.
443 = 0x01bb
Lets look at a better example.
Code: Select all
Micro Python v1.1.1-60-g8993fb6-dirty on 2014-06-30; UNIX version
>>> import struct
>>> struct.unpack('<h', b'\x00\x01')
(256,)
>>> struct.unpack('>h', b'\x00\x01')
(1,)
>h says to interpret the bytes as big-endian, to the '\x00' will be the MSB and the '\x01' will be the LSB. Thus we get 0x0001 or 1
443 = 0x01BB not 0x1B1B
And here's how to explain your examples:
Code: Select all
>>>struct.unpack('>h', '\x1b\x1b')
(6939,)
Code: Select all
>>>struct.unpack('>h', '\x1b')
(6912,)
Code: Select all
>>>struct.unpack('>h', '1b')
(12642,)
Code: Select all
>>>struct.unpack('>h', '1b1b')
(12642,)
443 = 0x01bb
Code: Select all
>>> struct.unpack('>h', b'\x01\xbb')
(443,)
Re: I2C, MSB, LSB, endianness and I'm in over my head
It also should be noted that bits and bytes should not be mixed up. Term "endianness" usually applies to how bytes are laid out in words. Terms "LSB" and "MSB" usually (perhaps less universally) apply to bits. Of course, someone may use these terms in different meanings, but then one targets for confusion.
Going next, when dealing with serial protocols, there's a question which bit is transferred first - LSB or MSB. Term "endianness" doesn't occur on the level of hardware serial protocols at all. What's being transferred is just a stream of bytes, each byte either LSB or MSB first, that's all. Interpretation of those bytes occur on another level, that's where endianness questions may arise.
Going next, when dealing with serial protocols, there's a question which bit is transferred first - LSB or MSB. Term "endianness" doesn't occur on the level of hardware serial protocols at all. What's being transferred is just a stream of bytes, each byte either LSB or MSB first, that's all. Interpretation of those bytes occur on another level, that's where endianness questions may arise.
Awesome MicroPython list
Pycopy - A better MicroPython https://github.com/pfalcon/micropython
MicroPython standard library for all ports and forks - https://github.com/pfalcon/micropython-lib
More up to date docs - http://pycopy.readthedocs.io/
Pycopy - A better MicroPython https://github.com/pfalcon/micropython
MicroPython standard library for all ports and forks - https://github.com/pfalcon/micropython-lib
More up to date docs - http://pycopy.readthedocs.io/
-
- Posts: 288
- Joined: Sun May 04, 2014 8:54 am
Re: I2C, MSB, LSB, endianness and I'm in over my head
Thanks a lot for the examples!
So struct.unpack needs to be passed a string (b'' > bytestring?) with two Bytes in it.
I somehow treated it as function that converts hex to int.
Could you explain the math behind '0x1b1b is equal to 6939'?
When I take 0x1b1b as a hex, I would expect 443.
64 32 16 1 (edit: <- this is wrong, it goes: 4096 256 16 1)
1 b 1 b
1x64 + 11*32 + 1*16 + 1*11 = 443
Obviously, that's not how MSB/LSB works.
Also, despite being wrong, 443 would better fit to what's in the datasheet^^
http://www.adafruit.com/datasheets/BST- ... 000-09.pdf page 15
Thanks for taking the time. Somehow Wikipedia and stackoverflow don't make sense to me on this.
So struct.unpack needs to be passed a string (b'' > bytestring?) with two Bytes in it.
I somehow treated it as function that converts hex to int.
Could you explain the math behind '0x1b1b is equal to 6939'?
When I take 0x1b1b as a hex, I would expect 443.
64 32 16 1 (edit: <- this is wrong, it goes: 4096 256 16 1)
1 b 1 b
1x64 + 11*32 + 1*16 + 1*11 = 443
Obviously, that's not how MSB/LSB works.
Also, despite being wrong, 443 would better fit to what's in the datasheet^^
http://www.adafruit.com/datasheets/BST- ... 000-09.pdf page 15
True! It was a typo in the OP, I meant Bytes.It also should be noted that bits and bytes should not be mixed up.
Thanks for taking the time. Somehow Wikipedia and stackoverflow don't make sense to me on this.
Last edited by Turbinenreiter on Tue Jul 01, 2014 7:14 pm, edited 1 time in total.
-
- Posts: 288
- Joined: Sun May 04, 2014 8:54 am
Re: I2C, MSB, LSB, endianness and I'm in over my head
Thin I got it.
(MSB -> hex to int) * 256 + (LSB -> hex to int)
b'\x1b\x1b'
\x1b -> 27
27 * 256 + 27 = 6939
... but why 256?
(MSB -> hex to int) * 256 + (LSB -> hex to int)
b'\x1b\x1b'
\x1b -> 27
27 * 256 + 27 = 6939
... but why 256?
Re: I2C, MSB, LSB, endianness and I'm in over my head
There are 8 bits in a byte. 2 to the power 8 = 256.Turbinenreiter wrote:Thin I got it.
(MSB -> hex to int) * 256 + (LSB -> hex to int)
b'\x1b\x1b'
\x1b -> 27
27 * 256 + 27 = 6939
... but why 256?
-
- Posts: 288
- Joined: Sun May 04, 2014 8:54 am
Re: I2C, MSB, LSB, endianness and I'm in over my head
... yeah.
Mixed up my hex and my binary tables.
head = spinning
this is it:
4096 256 16 1
1 b 1 b
1x4096 + 11*256 + 1*16 + 1*11 = 6939
Mixed up my hex and my binary tables.
head = spinning
this is it:
4096 256 16 1
1 b 1 b
1x4096 + 11*256 + 1*16 + 1*11 = 6939
Re: I2C, MSB, LSB, endianness and I'm in over my head
Looking at the datahseet, the calibration is stored at addresses 0xAA thru 0xBF.
Did you get the 0x1b1b from the correct location?
It looks like the chip-id of 0x55 is stored at address 0xD0 (so you can use that to verify that you're reading what you think you should be reading.
Did you get the 0x1b1b from the correct location?
It looks like the chip-id of 0x55 is stored at address 0xD0 (so you can use that to verify that you're reading what you think you should be reading.
-
- Posts: 288
- Joined: Sun May 04, 2014 8:54 am
Re: I2C, MSB, LSB, endianness and I'm in over my head
[I will repurpose this thread as 'BMP180 module development']
@dhylands: I'm quite sure it's from the rigth location. Reading 0xD0 also gives me the correct chip-id of 0x55 (altough it was printed out as b'U' - which took me a while to figure out^^)
I now have a (rough) implementation of a class for the BMP180. The values are off, but they react correctly, as in if I put my finger on the sensor the temperature rises and if I blow on it the pressure does to. I guess it's just some errors in the equations, there are quite some and long also to calculate the true values from the raw ones. But that's just bugs to find.
But it leads me to another question:
Right now I have implemented everything in a class.
That means when I use it as a module, it looks like this:
... but the sensor = bmp180.bmp180() part seems rather ugly ... .
Is there a better way to do this?
I would like it to look something like that:
... without having to create an Obkect from the class, but rather have already an object imported.
Here is the whole source of the module. Don't go hunting for bugs, but comments on the code are appreciated! I will also have to rethink which variables need to be self. and which not.
edit: also: is class.__dict__ not implemented yet (I'm still on the version the board shipped with, need to update that)?
@dhylands: I'm quite sure it's from the rigth location. Reading 0xD0 also gives me the correct chip-id of 0x55 (altough it was printed out as b'U' - which took me a while to figure out^^)
I now have a (rough) implementation of a class for the BMP180. The values are off, but they react correctly, as in if I put my finger on the sensor the temperature rises and if I blow on it the pressure does to. I guess it's just some errors in the equations, there are quite some and long also to calculate the true values from the raw ones. But that's just bugs to find.
But it leads me to another question:
Right now I have implemented everything in a class.
That means when I use it as a module, it looks like this:
Code: Select all
import bmp180
sensor = bmp180.bmp180() # but this seems ugly ...
print(sensor.get_altitude())
Is there a better way to do this?
I would like it to look something like that:
Code: Select all
import bmp180
print(bmp180.get_altitude())
Here is the whole source of the module. Don't go hunting for bugs, but comments on the code are appreciated! I will also have to rethink which variables need to be self. and which not.
Code: Select all
import struct
import pyb
class bmp180():
def __init__(self):
self.oss = 3
self.bmp = pyb.I2C(2, pyb.I2C.MASTER)
self.bmp_addr = self.bmp.scan()[0]
self.chip_id = self.bmp.mem_read(2, self.bmp_addr, 0xAA)
self.AC1 = struct.unpack('>h', self.bmp.mem_read(2, self.bmp_addr, 0xAA))[0]
self.AC2 = struct.unpack('>h', self.bmp.mem_read(2, self.bmp_addr, 0xAC))[0]
self.AC3 = struct.unpack('>h', self.bmp.mem_read(2, self.bmp_addr, 0xAE))[0]
self.AC4 = struct.unpack('>H', self.bmp.mem_read(2, self.bmp_addr, 0xB0))[0]
self.AC5 = struct.unpack('>H', self.bmp.mem_read(2, self.bmp_addr, 0xB2))[0]
self.AC6 = struct.unpack('>H', self.bmp.mem_read(2, self.bmp_addr, 0xB4))[0]
self.B1 = struct.unpack('>h', self.bmp.mem_read(2, self.bmp_addr, 0xB6))[0]
self.B2 = struct.unpack('>h', self.bmp.mem_read(2, self.bmp_addr, 0xB8))[0]
self.MB = struct.unpack('>h', self.bmp.mem_read(2, self.bmp_addr, 0xBA))[0]
self.MC = struct.unpack('>h', self.bmp.mem_read(2, self.bmp_addr, 0xBC))[0]
self.MD = struct.unpack('>h', self.bmp.mem_read(2, self.bmp_addr, 0xBE))[0]
def read_uncomp_temp(self):
self.bmp.mem_write(0x2E, self.bmp_addr, 0xF4)
pyb.delay(5)
self.UT = struct.unpack('>h', self.bmp.mem_read(2, self.bmp_addr, 0xF6))[0]
return self.UT
def read_uncomp_pressure(self, oss):
self.bmp.mem_write((0x34+(self.oss<<6)), self.bmp_addr, 0xF4)
pyb.delay((self.oss+1)*7)
self.MSB = struct.unpack('<h', self.bmp.mem_read(1, self.bmp_addr, 0xF6))[0]
self.LSB = struct.unpack('<h', self.bmp.mem_read(1, self.bmp_addr, 0xF7))[0]
self.XLSB = struct.unpack('<h', self.bmp.mem_read(1, self.bmp_addr, 0xF8))[0]
self.UP = ((self.MSB<<16)+(self.LSB<<8)+self.XLSB)>>(8-self.oss)
return self.UP
def calc_temp(self, UT):
self.X1 = (self.UT-self.AC6)*self.AC5/2**15
self.X2 = self.MC*2**11/(self.X1+self.MD)
self.B5 = self.X1+self.X2
self.T = (self.B5+8)/2**4
return self.T, self.B5
def calc_pressure(self, UP, oss):
self.B5 = self.calc_temp(self.read_uncomp_temp())[1]
self.B6 = self.B5-4000
self.X1 = (self.B2*(self.B6*self.B6/2**12))/2**11
self.X2 = self.AC2*self.B6/2**11
self.X3 = self.X1+self.X2
self.B3 = ((int((self.AC1*4+self.X3))<<self.oss)+2)/4
self.X1 = self.AC3*self.B6/2**13
self.X2 = (self.B1*(self.B6*self.B6/2**12))/2**16
self.X3 = ((self.X1+self.X2)+2)/2**2
self.B4 = self.AC4*(self.X3+32768)/2**15
self.B7 = (self.UP-self.B3)*(5000>>self.oss)
if self.B7 < 0x80000000: self.p = (self.B7*2)/self.B4
else: self.p = (self.B7/self.B4)*2
self.X1 = (self.p/2**8)**2
self.X1 = (self.X1*3038)/2**16
self.X2 = (-7357*self.p)/2**16
self.p = self.p+(self.X1+self.X2+3791)/2**4
return self.p
def get_temp(self):
return self.calc_temp(self.read_uncomp_temp())[0]
def get_pressure(self, oss):
return self.calc_pressure(self.read_uncomp_pressure(self.oss), self.oss)
def get_temp_and_pressure(self, oss):
return self.get_temp(), self.get_pressure(self.oss)
def get_altitude(self):
self.h = 44330*(1-(self.get_pressure(3)/1013.25)**(1/5.255))
return self.h
Re: development of a module for the bmp180 pressure sensor
You can do:
and:
But I don't think it is a good way to do; you should let the main module instanciate the object.
Code: Select all
import struct
import pyb
class BMP180():
def __init__(self):
...
bmp180 = BMP180()
Code: Select all
from bmp180 import bmp180
bmp180.get_altitude()
Code: Select all
from bmp180 import BMP180
bmp180 = BMP180()
Frédéric