Created
August 20, 2018 17:04
-
-
Save jasonmhite/f92c3e6a341312e178f2288ac2a8543a to your computer and use it in GitHub Desktop.
BMP280 pyFTDI
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import pyftdi as F | |
from pyftdi import i2c | |
from ctypes import c_short | |
from time import sleep | |
import atexit | |
### Some utility functions | |
# These both assume the second byte is the higher bits. | |
# Concat two adacent bytes to a signed short | |
def getShort(data, index): | |
return c_short((data[index+1] << 8) + data[index]).value | |
# Concat two adacent bytes to an unsigned short | |
def getUShort(data, index): | |
return (data[index+1] << 8) + data[index] | |
### Important register addresses | |
# See the datasheet. | |
# i2c adress. | |
BME_ADDR = 0x76 | |
# Register that contains raw measurement values (ro). | |
REG_DATA = 0xF7 | |
# Controls for oversampling and power options (rw). | |
REG_CONTROL = 0xF4 | |
# Controls sampling rate, filter coefficients and interface (rw, but | |
# writes may be ignored unless the device is in sleep mode). | |
# I will be leaving this as the default settings, it's just here for reference. | |
REG_CONFIG = 0xF5 | |
# Calibration data registers (ro) | |
REG_CAL1 = 0x88 | |
REG_CAL2 = 0xA1 # Not used (is for the BME280) | |
# Reset register, writing the value 0xB6 will trigger a soft reset. | |
REG_RESET = 0xE0 | |
### Calculate the values for configuration. | |
# I will use forced mode, where it takes one measurement and then goes | |
# to sleep. You can also put it in free-run mode, where it constantly | |
# takes measurements, but forced mode is more appropriate here. | |
# | |
# You need to write the control register every time | |
# you want a new measurement in this mode. | |
OVERSAMPLE_TEMP = 2 # 2x oversampling (0b010) | |
OVERSAMPLE_PRES = 2 # 2x oversampling (0b010) | |
MODE = 1 # Forced mode, take one measurement then sleep (0b1) | |
control = OVERSAMPLE_TEMP << 5 | OVERSAMPLE_PRES << 2 | MODE | |
# 0b01000000 0b00001000 0b00000001 | |
# -------------------------------------------------------- | |
# = 0b01001001 | |
### Set up i2c. | |
# Same deal as the SPI example | |
ctrl = i2c.I2cController() | |
ctrl.configure('ftdi://ftdi:232h/1') | |
atexit.register(ctrl.terminate) | |
# Port here automatically prepends the i2c slave address to all transactions. | |
port = ctrl.get_port(BME_ADDR) | |
### Now let's start talking to the chip! | |
# Force a reset to start fresh. Device will start in sleep mode (not taking | |
# measurements. | |
port.write_to(REG_RESET, [0xB6]) | |
# Read the chip id, you can use this to distinguish between different chips | |
# in the line. All the chips have the same interface, but some have more | |
# sensors. E.g. the BMP280 is pressure and temperature, BME280 is the same thing | |
# but also humidity. Reading pressure and temperature is exactly the same | |
# between the two. | |
# If you adapt this code for the BME280, you need to change the i2c address | |
# and add code to also read the humidity. | |
chip_id = port.read_from(0xD0, 1) # Should be 0x58 == 88 for production BMP280. | |
# Set oversampling and activate forced mode. | |
port.write_to(REG_CONTROL, [control]) | |
### Read values and do the conversion | |
# The following procedure just comes from the datasheet, we're just reading | |
# out register values and converting them. | |
# Read calibration. This is fixed and only needs to be done once. | |
cal1 = port.read_from(REG_CAL1, 24) | |
cal2 = port.read_from(REG_CAL2, 1) # Not used, will be zero | |
# Convert the raw bytes to the calibration coefficients. Ditto, only | |
# needs to be done once. | |
dig_T1 = getUShort(cal1, 0) | |
dig_T2 = getShort(cal1, 2) | |
dig_T3 = getShort(cal1, 4) | |
dig_P1 = getUShort(cal1, 6) | |
dig_P2 = getShort(cal1, 8) | |
dig_P3 = getShort(cal1, 10) | |
dig_P4 = getShort(cal1, 12) | |
dig_P5 = getShort(cal1, 14) | |
dig_P6 = getShort(cal1, 16) | |
dig_P7 = getShort(cal1, 18) | |
dig_P8 = getShort(cal1, 20) | |
dig_P9 = getShort(cal1, 22) | |
# Sleep long enough for a measurement to complete. You can calculate | |
# the minimum wait time using a formula in the datasheet or you can also | |
# look at the status register if you want. Here I'm just sleeping for | |
# a relatively long time. | |
# The actual sampling time depends on oversampling settings. | |
sleep(0.100) | |
# Read the raw measurement data from the device. These are in the form | |
# of raw ADC readings, which must be converted using the device-specific | |
# calibration and the procedure in the datasheet. | |
data = port.read_from(REG_DATA, 8) | |
# Cat together the bytes to get the raw integer pressure and temp readings. | |
pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4) | |
temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4) | |
# Formulas... first calculate the temperature. | |
var1 = ((((temp_raw >> 3) - (dig_T1 << 1))) * (dig_T2)) >> 11 | |
var2 = (((((temp_raw >> 4) - (dig_T1)) * ((temp_raw >> 4) - (dig_T1))) >> 12) * (dig_T3)) >> 14 | |
t_fine = var1 + var2 | |
temperature = float(((t_fine * 5) + 128) >> 8); | |
# Now calculate some values that are used to compensate the pressure reading | |
# for temperature. | |
var1 = t_fine / 2.0 - 64000.0 | |
var2 = var1 * var1 * dig_P6 / 32768.0 | |
var2 = var2 + var1 * dig_P5 * 2.0 | |
var2 = var2 / 4.0 + dig_P4 * 65536.0 | |
var1 = (dig_P3 * var1 * var1 / 524288.0 + dig_P2 * var1) / 524288.0 | |
var1 = (1.0 + var1 / 32768.0) * dig_P1 | |
# Use the above values to compensate the base pressure reading for | |
# temperature. | |
if var1 == 0: | |
pressure = 0 # An error occurred with the reading so skip this. | |
else: | |
pressure = 1048576.0 - pres_raw | |
pressure = ((pressure - var2 / 4096.0) * 6250.0) / var1 | |
var1 = dig_P9 * pressure * pressure / 2147483648.0 | |
var2 = pressure * dig_P8 / 32768.0 | |
pressure = pressure + (var1 + var2 + dig_P7) / 16.0 | |
# Print out the readings. | |
print("id: 0x{:x}".format(chip_id[0])) | |
print("temp: {0:.2f}C".format(temperature / 100.0)) | |
print("pressure: {0:.2f}hPa".format(pressure / 100.0)) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment