Mini estación meteorológica Raspberry

Propósito:

Construir una estación metereológica basada en los sensores BME280 y BH1750 local que envía datos a un servidor externo.

Un proyecto interesante es una estación meteorológica que envía los datos de presión, temperatura, humedad e iluminación a un servidor externo para proporcionar una gráfica, en este caso Thingspeak.

También puede servir para ver cómo funciona el internet de las cosas o IoT.

Material necesario

Dificultad:

  • Fácil

Conexiones:

Comenzamos conectando los sensores BME280 y BH1750. El esquema es el mismo:

SDA -> GPIO SDA1

SCL ->  GPIO SCL1

GND -> GND

VCC -> 3.3V

Comenzamos comprobando si está activado el driver i2c.

dmesg | grep i2c

En caso contrario, tendremos que activarlo con

sudo raspi-config

Vamos a 9 Advanced Options

Bajamos hasta A7 I2C

Y lo activamos.

Instalamos algunos paquetes

sudo apt-get install i2c-tools python-pip

Hacemos lo mismo con Python, en su versión 2 o 3:

sudo python setup.py install

sudo python3 setup.py install

E instalamos la librería RPi.bme280

sudo pip install RPi.bme280

Comprobamos el correcto funcionamiento con el siguiente script:

#!/usr/bin/python
#--------------------------------------
#    ___  ___  _ ____
#   / _ \/ _ \(_) __/__  __ __
#  / , _/ ___/ /\ \/ _ \/ // /
# /_/|_/_/  /_/___/ .__/\_, /
#                /_/   /___/
#
#           bme280.py
#  Read data from a digital pressure sensor.
#
#  Official datasheet available from :
#  https://www.bosch-sensortec.com/bst/products/all_products/bme280
#
# Author : Matt Hawkins
# Date   : 25/07/2016
#
# http://www.raspberrypi-spy.co.uk/
#
# Modified for personal use by
# https://github.com/bortek
# 
#--------------------------------------
import smbus
import time
from ctypes import c_short
from ctypes import c_byte
from ctypes import c_ubyte
import datetime

# Humidity  & temp adjustment value. Positive value will be added.
# Negative will be substracted
humidity_adj = +0
temperature_adj = +0

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_CONTROL_HUM = 0xF2
  REG_HUM_MSB = 0xFD
  REG_HUM_LSB = 0xFE

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

  # Oversample setting for humidity register - page 26
  OVERSAMPLE_HUM = 2
  bus.write_byte_data(addr, REG_CONTROL_HUM, OVERSAMPLE_HUM)

  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)

  # Wait in ms (Datasheet Appendix B: Measurement time and current calculation)
  wait_time = 1.25 + (2.3 * OVERSAMPLE_TEMP) + ((2.3 * OVERSAMPLE_PRES) + 0.575) + ((2.3 * OVERSAMPLE_HUM)+0.575)
  time.sleep(wait_time/1000)  # Wait the required time  

  # 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
  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.0 * 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

def main():

  (chip_id, chip_version) = readBME280ID()
#  print "Chip ID     :", chip_id
#  print "Version     :", chip_version

  temperature,pressure,humidity = readBME280All()

  if datetime.date.today().year != 1970:
    print ("{} {:0.1f} {:0.1f} {:0.2f}".format(datetime.datetime.today().strftime('%Y-%m-%d %H:%M:%S'),humidity + humidity_adj,temperature + temperature_adj,pressure))

#  print "Temperature : ", temperature, "C"
#  print "Pressure : ", pressure, "hPa"
#  print "Humidity : ", humidity, "%"

if __name__=="__main__":
   main()

Claro, que también lo podemos instalar así

wget -O bme280.py http://bit.ly/bme280py

o así:

wget https://bitbucket.org/MattHawkinsUK/rpispy-misc/raw/master/python/bme280.py

Si da error, instalamos la librería smbus

pip install smbus o pip3 install smbus (en su caso)

ejecutando

python bme280.py

debe aparecer

(debería también dar datos de humedad… si fuera un bme280. En mi caso me temo que sea un bmp280).

También comprobamos el funcionamiento del sensor de luminosidad, con un script que se puede descargar de aquí:

https://gist.github.com/oskar456/95c66d564c58361ecf9f

En este caso, el resultado es este.

Ya que tenemos los sensores funcionando, nos creamos una cuenta gratuita en https://thingspeak.com/.

Con este script leemos los datos de los sensores y los enviamos periódicamente a https://thingspeak.com/, con este resultado:

También presenta los datos en pantalla:

Fuentes:

https://pypi.org/project/RPi.bme280/

Using the BH1750FVI I2C Digital Light Sensor

Conclusión:

Una raspberry puede ser el «corazón» de una estación meteorológica con unos sensores adecuados.

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *