Monday, August 22, 2016

Logging BME280 Weather Data

LATEST UPDATE: a replacement bme280 has been installed and has been running with no problems for a day or so; the 'failed' unit apparently came back to life and has been running inside with no problem - one unit reads 10 percent too high on humidity - i'm wondering if these aren't Bosch Sensortec units?

UPDATE: the bme280 sensor FAILED after four hours of use and queries on one-minute intervals; i'm disappointed, but may try another one to see if it was an anomaly (hey, shit happens, right?)

fortunately, i had a spare bmp180 on hand, and have put that into use instead - we'll see how it holds up... i'm sure i'm not the first n00b to run across sensor hardware failures  - i've even seen pictures of a bme280 melted from what i can only assume is an over- or reverse-voltage run...

:-)

just spent a bit of a morning crafting a logging script to read bme280 sensor data... the file has a format like this:

2016-08-22 12:17 78.80 30.14 52.60
2016-08-22 12:17 78.33 30.14 52.57
2016-08-22 12:18 78.28 30.14 52.56
...
  
the bme280 is an interesting sensor that returns temperature, barometric pressure and humidty...

HOWEVER, many users report that the temperature runs about 4F higher than that reported by most other sensors... and i found this to be true, as i gauged the bme280's temp readout against a bmp180 and an LCD digital thermometer... in light of this, i've included a 4-degree 'correction' in the conversion code in the script - just so you know why

i installed the lighttpd web server on a Raspberry Pi 2, with the sensor attached and fed out to the side of the garage... the script logs data once a minute (the log will grow to 50MB over the course of a year)... current weather info can be retrieved via a simple one-liner shell script:


#!/bin/sh
# retrieve weather info from data log
# 3 - temperature
# 4 - barometric pressure
# 5 - relative humidity
curl http://192.168.1.20/weather.txt -s -d ascii | tail -1 | cut -f $1 -d ' '


here's the logging script in Python:

#!/usr/bin/python
# version 0.1 - bme280 data reader and logger
# 082216
# added 1-minute logging
#
# data fields:
#     DATE         TEMP  BARO  HUMID
# YYYY-MM-DD HH:MM TT.TT PP.PP HH.HH

import smbus
import time
from datetime import datetime
from ctypes import c_short
from ctypes import c_byte
from ctypes import c_ubyte

DEVICE = 0x76 # Default device I2C address

bus = smbus.SMBus(1) # Rev 2 Pi, Pi 2 & Pi 3 uses bus 1
                     # Rev 1 Pi uses bus 0

def getShort(data, index):
  # return two bytes from data as a signed 16-bit value
  return c_short((data[index+1] << 8) + data[index]).value

def getUShort(data, index):
  # return two bytes from data as an unsigned 16-bit value
  return (data[index+1] << 8) + data[index]


def getChar(data,index):
  # return one byte from data as a signed char
  result = data[index]
  if result > 127:
    result -= 256
  return result

def getUChar(data,index):
  # return one byte from data as an unsigned char
  result =  data[index] & 0xFF
  return result

def readBME280ID(addr=DEVICE):
  # Chip ID Register Address
  REG_ID     = 0xD0
  (chip_id, chip_version) = bus.read_i2c_block_data(addr, REG_ID, 2)
  return (chip_id, chip_version)

def readBME280All(addr=DEVICE):
  # Register Addresses
  REG_DATA = 0xF7
  REG_CONTROL = 0xF4
  REG_CONFIG  = 0xF5

  REG_HUM_MSB = 0xFD
  REG_HUM_LSB = 0xFE

  # Oversample setting - page 27
  OVERSAMPLE_TEMP = 2
  OVERSAMPLE_PRES = 2
  MODE = 1

  control = OVERSAMPLE_TEMP<<5 | OVERSAMPLE_PRES<<2 | MODE
  bus.write_byte_data(addr, REG_CONTROL, control)


  # Read blocks of calibration data from EEPROM
  # See Page 22 data sheet
  cal1 = bus.read_i2c_block_data(addr, 0x88, 24)
  cal2 = bus.read_i2c_block_data(addr, 0xA1, 1)
  cal3 = bus.read_i2c_block_data(addr, 0xE1, 7)

  # Convert byte data to word values
  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)

  dig_H1 = getUChar(cal2, 0)
  dig_H2 = getShort(cal3, 0)
  dig_H3 = getUChar(cal3, 2)

  dig_H4 = getChar(cal3, 3)
  dig_H4 = (dig_H4 << 24) >> 20
  dig_H4 = dig_H4 | (getChar(cal3, 4) & 0x0F)

  dig_H5 = getChar(cal3, 5)
  dig_H5 = (dig_H5 << 24) >> 20
  dig_H5 = dig_H5 | (getUChar(cal3, 4) >> 4 & 0x0F)

  dig_H6 = getChar(cal3, 6)



  # Read temperature/pressure/humidity
  data = bus.read_i2c_block_data(addr, REG_DATA, 8)
  pres_raw = (data[0] << 12) | (data[1] << 4) | (data[2] >> 4)
  temp_raw = (data[3] << 12) | (data[4] << 4) | (data[5] >> 4)
  hum_raw = (data[6] << 8) | data[7]

  #Refine temperature
  var1 = ((((temp_raw>>3)-(dig_T1<<1)))*(dig_T2)) >> 11^M  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);

  # Refine pressure and adjust 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
  if var1 == 0:
    pressure=0
  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


  # Refine humidity
  humidity = t_fine - 76800.0
  humidity = (hum_raw - (dig_H4 * 64.0 + dig_H5 / 16384.8 * humidity)) * (dig_H2 / 65536.0 * (1.0 + dig_H6 / 67108864.0 * humidity * (1.0 + dig_H3 / 67108864.0 * humidity)))
  humidity = humidity * (1.0 - dig_H1 * humidity / 524288.0)
  if humidity > 100:
    humidity = 100
  elif humidity < 0:
    humidity = 0

  return temperature/100.0,pressure/100.0,humidity

while True:
 temperature,pressure,humidity = readBME280All()

 text_file = open("/var/www/html/weather.txt", "a")
 text_file.write(str(datetime.now().strftime('%F %H:%M ')))

 # correct for bme280's high temp readout
 text_file.write("%.2f " % (((temperature)*9/5)+32-4))
 text_file.write("%.2f " % ((pressure)/33.8638866667))
 text_file.write("%.2f\n" % humidity)
 text_file.close()
 time.sleep(60)



No comments:

Post a Comment