Reorganisation
Add pyportal-controller "sub-project"
This commit is contained in:
		
							
								
								
									
										277
									
								
								station-esp8266/lib-src/adafruit_max31865.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										277
									
								
								station-esp8266/lib-src/adafruit_max31865.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,277 @@
 | 
			
		||||
# The MIT License (MIT)
 | 
			
		||||
#
 | 
			
		||||
# Copyright (c) 2017 Tony DiCola for Adafruit Industries
 | 
			
		||||
#
 | 
			
		||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
 | 
			
		||||
# of this software and associated documentation files (the "Software"), to deal
 | 
			
		||||
# in the Software without restriction, including without limitation the rights
 | 
			
		||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 | 
			
		||||
# copies of the Software, and to permit persons to whom the Software is
 | 
			
		||||
# furnished to do so, subject to the following conditions:
 | 
			
		||||
#
 | 
			
		||||
# The above copyright notice and this permission notice shall be included in
 | 
			
		||||
# all copies or substantial portions of the Software.
 | 
			
		||||
#
 | 
			
		||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 | 
			
		||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 | 
			
		||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 | 
			
		||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 | 
			
		||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 | 
			
		||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 | 
			
		||||
# THE SOFTWARE.
 | 
			
		||||
"""
 | 
			
		||||
`adafruit_max31865`
 | 
			
		||||
====================================================
 | 
			
		||||
 | 
			
		||||
MicroPython module for the MAX31865 platinum RTD temperature sensor.  See
 | 
			
		||||
examples/simpletest.py for an example of the usage.
 | 
			
		||||
 | 
			
		||||
* Author(s): Tony DiCola
 | 
			
		||||
* micropython adaptation : arofarn
 | 
			
		||||
 | 
			
		||||
Implementation Notes
 | 
			
		||||
--------------------
 | 
			
		||||
 | 
			
		||||
**Hardware:**
 | 
			
		||||
 | 
			
		||||
* Adafruit `Universal Thermocouple Amplifier MAX31856 Breakout
 | 
			
		||||
  <https://www.adafruit.com/product/3263>`_ (Product ID: 3263)
 | 
			
		||||
 | 
			
		||||
* Adafruit `PT100 RTD Temperature Sensor Amplifier - MAX31865
 | 
			
		||||
  <https://www.adafruit.com/product/3328>`_ (Product ID: 3328)
 | 
			
		||||
 | 
			
		||||
* Adafruit `PT1000 RTD Temperature Sensor Amplifier - MAX31865
 | 
			
		||||
  <https://www.adafruit.com/product/3648>`_ (Product ID: 3648)
 | 
			
		||||
 | 
			
		||||
**Software and Dependencies:**
 | 
			
		||||
 | 
			
		||||
* Adafruit CircuitPython firmware for the ESP8622 and M0-based boards:
 | 
			
		||||
  https://github.com/adafruit/circuitpython/releases
 | 
			
		||||
"""
 | 
			
		||||
import math
 | 
			
		||||
import time
 | 
			
		||||
from machine import Pin
 | 
			
		||||
 | 
			
		||||
from micropython import const
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
__version__ = "0.0.0-auto.0"
 | 
			
		||||
__repo__ = "https://github.com/adafruit/Adafruit_CircuitPython_MAX31865.git"
 | 
			
		||||
 | 
			
		||||
# pylint: disable=bad-whitespace
 | 
			
		||||
# Register and other constant values:
 | 
			
		||||
_MAX31865_CONFIG_REG = const(0x00)
 | 
			
		||||
_MAX31865_CONFIG_BIAS = const(0x80)
 | 
			
		||||
_MAX31865_CONFIG_MODEAUTO = const(0x40)
 | 
			
		||||
_MAX31865_CONFIG_MODEOFF = const(0x00)
 | 
			
		||||
_MAX31865_CONFIG_1SHOT = const(0x20)
 | 
			
		||||
_MAX31865_CONFIG_3WIRE = const(0x10)
 | 
			
		||||
_MAX31865_CONFIG_24WIRE = const(0x00)
 | 
			
		||||
_MAX31865_CONFIG_FAULTSTAT = const(0x02)
 | 
			
		||||
_MAX31865_CONFIG_FILT50HZ = const(0x01)
 | 
			
		||||
_MAX31865_CONFIG_FILT60HZ = const(0x00)
 | 
			
		||||
_MAX31865_RTDMSB_REG = const(0x01)
 | 
			
		||||
_MAX31865_RTDLSB_REG = const(0x02)
 | 
			
		||||
_MAX31865_HFAULTMSB_REG = const(0x03)
 | 
			
		||||
_MAX31865_HFAULTLSB_REG = const(0x04)
 | 
			
		||||
_MAX31865_LFAULTMSB_REG = const(0x05)
 | 
			
		||||
_MAX31865_LFAULTLSB_REG = const(0x06)
 | 
			
		||||
_MAX31865_FAULTSTAT_REG = const(0x07)
 | 
			
		||||
_MAX31865_FAULT_HIGHTHRESH = const(0x80)
 | 
			
		||||
_MAX31865_FAULT_LOWTHRESH = const(0x40)
 | 
			
		||||
_MAX31865_FAULT_REFINLOW = const(0x20)
 | 
			
		||||
_MAX31865_FAULT_REFINHIGH = const(0x10)
 | 
			
		||||
_MAX31865_FAULT_RTDINLOW = const(0x08)
 | 
			
		||||
_MAX31865_FAULT_OVUV = const(0x04)
 | 
			
		||||
_RTD_A = 3.9083e-3
 | 
			
		||||
_RTD_B = -5.775e-7
 | 
			
		||||
# pylint: enable=bad-whitespace
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MAX31865:
 | 
			
		||||
    """Driver for the MAX31865 thermocouple amplifier."""
 | 
			
		||||
 | 
			
		||||
    def __init__(self, spi, cs, *, rtd_nominal=100, ref_resistor=430.0,
 | 
			
		||||
                 wires=2, filter_frequency=60.0):
 | 
			
		||||
        self.rtd_nominal = rtd_nominal
 | 
			
		||||
        self.ref_resistor = ref_resistor
 | 
			
		||||
        self._spi = spi
 | 
			
		||||
        self._cs = cs
 | 
			
		||||
        self._cs.init(mode=Pin.OUT, pull=Pin.PULL_UP)
 | 
			
		||||
        self._cs.value(1)
 | 
			
		||||
 | 
			
		||||
        # Set 50Hz or 60Hz filter.
 | 
			
		||||
        if filter_frequency not in (50, 60):
 | 
			
		||||
            raise ValueError("Filter_frequency must be a value of 50 or 60!")
 | 
			
		||||
        config = self._read_u8(_MAX31865_CONFIG_REG)
 | 
			
		||||
        if filter_frequency == 50:
 | 
			
		||||
            config |= _MAX31865_CONFIG_FILT50HZ
 | 
			
		||||
        else:
 | 
			
		||||
            config &= ~_MAX31865_CONFIG_FILT50HZ
 | 
			
		||||
 | 
			
		||||
        # Set wire config register based on the number of wires specified.
 | 
			
		||||
        if wires not in (2, 3, 4):
 | 
			
		||||
            raise ValueError("Wires must be a value of 2, 3, or 4!")
 | 
			
		||||
        if wires == 3:
 | 
			
		||||
            config |= _MAX31865_CONFIG_3WIRE
 | 
			
		||||
        else:
 | 
			
		||||
            # 2 or 4 wire
 | 
			
		||||
            config &= ~_MAX31865_CONFIG_3WIRE
 | 
			
		||||
        self._write_u8(_MAX31865_CONFIG_REG, config)
 | 
			
		||||
        # Default to no bias and no auto conversion.
 | 
			
		||||
        self.bias = False
 | 
			
		||||
        self.auto_convert = False
 | 
			
		||||
 | 
			
		||||
    # pylint: disable=no-member
 | 
			
		||||
    def _read_u8(self, address):
 | 
			
		||||
        # Read an 8-bit unsigned value from the specified 8-bit address.
 | 
			
		||||
        buf = bytearray(1)
 | 
			
		||||
        self._cs.value(0)
 | 
			
		||||
        self._spi.write(bytes([address & 0x7F]))
 | 
			
		||||
        self._spi.readinto(buf, 1)
 | 
			
		||||
        self._cs.value(1)
 | 
			
		||||
        return buf[0]
 | 
			
		||||
 | 
			
		||||
    def _read_u16(self, address):
 | 
			
		||||
        # Read a 16-bit BE unsigned value from the specified 8-bit address.
 | 
			
		||||
        buf = bytearray(2)
 | 
			
		||||
        self._cs.value(0)
 | 
			
		||||
        self._spi.write(bytes([address & 0x7F]))
 | 
			
		||||
        self._spi.readinto(buf, 1)
 | 
			
		||||
        self._cs.value(1)
 | 
			
		||||
        return (buf[0] << 8) | buf[1]
 | 
			
		||||
 | 
			
		||||
    def _write_u8(self, address, val):
 | 
			
		||||
        # Write an 8-bit unsigned value to the specified 8-bit address.
 | 
			
		||||
        buf = bytearray(2)
 | 
			
		||||
        buf[0] = (address | 0x80) & 0xFF
 | 
			
		||||
        buf[1] = val & 0xFF
 | 
			
		||||
        self._cs.value(0)
 | 
			
		||||
        self._spi.write(buf)
 | 
			
		||||
        self._cs.value(1)
 | 
			
		||||
 | 
			
		||||
    # pylint: enable=no-member
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def bias(self):
 | 
			
		||||
        """State of the sensor's bias (True/False)."""
 | 
			
		||||
        return bool(self._read_u8(_MAX31865_CONFIG_REG) & _MAX31865_CONFIG_BIAS)
 | 
			
		||||
 | 
			
		||||
    @bias.setter
 | 
			
		||||
    def bias(self, val):
 | 
			
		||||
        config = self._read_u8(_MAX31865_CONFIG_REG)
 | 
			
		||||
        if val:
 | 
			
		||||
            config |= _MAX31865_CONFIG_BIAS  # Enable bias.
 | 
			
		||||
        else:
 | 
			
		||||
            config &= ~_MAX31865_CONFIG_BIAS  # Disable bias.
 | 
			
		||||
        self._write_u8(_MAX31865_CONFIG_REG, config)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def auto_convert(self):
 | 
			
		||||
        """State of the sensor's automatic conversion
 | 
			
		||||
        mode (True/False).
 | 
			
		||||
        """
 | 
			
		||||
        return bool(self._read_u8(_MAX31865_CONFIG_REG) & _MAX31865_CONFIG_MODEAUTO)
 | 
			
		||||
 | 
			
		||||
    @auto_convert.setter
 | 
			
		||||
    def auto_convert(self, val):
 | 
			
		||||
        config = self._read_u8(_MAX31865_CONFIG_REG)
 | 
			
		||||
        if val:
 | 
			
		||||
            config |= _MAX31865_CONFIG_MODEAUTO  # Enable auto convert.
 | 
			
		||||
        else:
 | 
			
		||||
            config &= ~_MAX31865_CONFIG_MODEAUTO  # Disable auto convert.
 | 
			
		||||
        self._write_u8(_MAX31865_CONFIG_REG, config)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def fault(self):
 | 
			
		||||
        """The fault state of the sensor.  Use ``clear_faults()`` to clear the
 | 
			
		||||
        fault state.  Returns a 6-tuple of boolean values which indicate if any
 | 
			
		||||
        faults are present:
 | 
			
		||||
 | 
			
		||||
        - HIGHTHRESH
 | 
			
		||||
        - LOWTHRESH
 | 
			
		||||
        - REFINLOW
 | 
			
		||||
        - REFINHIGH
 | 
			
		||||
        - RTDINLOW
 | 
			
		||||
        - OVUV
 | 
			
		||||
        """
 | 
			
		||||
        faults = self._read_u8(_MAX31865_FAULTSTAT_REG)
 | 
			
		||||
        # pylint: disable=bad-whitespace
 | 
			
		||||
        highthresh = bool(faults & _MAX31865_FAULT_HIGHTHRESH)
 | 
			
		||||
        lowthresh = bool(faults & _MAX31865_FAULT_LOWTHRESH)
 | 
			
		||||
        refinlow = bool(faults & _MAX31865_FAULT_REFINLOW)
 | 
			
		||||
        refinhigh = bool(faults & _MAX31865_FAULT_REFINHIGH)
 | 
			
		||||
        rtdinlow = bool(faults & _MAX31865_FAULT_RTDINLOW)
 | 
			
		||||
        ovuv = bool(faults & _MAX31865_FAULT_OVUV)
 | 
			
		||||
        # pylint: enable=bad-whitespace
 | 
			
		||||
        return (highthresh, lowthresh, refinlow, refinhigh, rtdinlow, ovuv)
 | 
			
		||||
 | 
			
		||||
    def clear_faults(self):
 | 
			
		||||
        """Clear any fault state previously detected by the sensor."""
 | 
			
		||||
        config = self._read_u8(_MAX31865_CONFIG_REG)
 | 
			
		||||
        config &= ~0x2C
 | 
			
		||||
        config |= _MAX31865_CONFIG_FAULTSTAT
 | 
			
		||||
        self._write_u8(_MAX31865_CONFIG_REG, config)
 | 
			
		||||
 | 
			
		||||
    def read_rtd(self):
 | 
			
		||||
        """Perform a raw reading of the thermocouple and return its 15-bit
 | 
			
		||||
        value.  You'll need to manually convert this to temperature using the
 | 
			
		||||
        nominal value of the resistance-to-digital conversion and some math.  If you just want
 | 
			
		||||
        temperature use the temperature property instead.
 | 
			
		||||
        """
 | 
			
		||||
        self.clear_faults()
 | 
			
		||||
        self.bias = True
 | 
			
		||||
        time.sleep(0.01)
 | 
			
		||||
        config = self._read_u8(_MAX31865_CONFIG_REG)
 | 
			
		||||
        config |= _MAX31865_CONFIG_1SHOT
 | 
			
		||||
        self._write_u8(_MAX31865_CONFIG_REG, config)
 | 
			
		||||
        time.sleep(0.065)
 | 
			
		||||
        rtd = self._read_u16(_MAX31865_RTDMSB_REG)
 | 
			
		||||
        # Remove fault bit.
 | 
			
		||||
        rtd >>= 1
 | 
			
		||||
        return rtd
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def resistance(self):
 | 
			
		||||
        """Read the resistance of the RTD and return its value in Ohms."""
 | 
			
		||||
        resistance = self.read_rtd()
 | 
			
		||||
        resistance /= 32768
 | 
			
		||||
        resistance *= self.ref_resistor
 | 
			
		||||
        return resistance
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def temperature(self):
 | 
			
		||||
        """Read the temperature of the sensor and return its value in degrees
 | 
			
		||||
        Celsius.
 | 
			
		||||
        """
 | 
			
		||||
        # This math originates from:
 | 
			
		||||
        # http://www.analog.com/media/en/technical-documentation/application-notes/AN709_0.pdf
 | 
			
		||||
        # To match the naming from the app note we tell lint to ignore the Z1-4
 | 
			
		||||
        # naming.
 | 
			
		||||
        # pylint: disable=invalid-name
 | 
			
		||||
        raw_reading = self.resistance
 | 
			
		||||
        Z1 = -_RTD_A
 | 
			
		||||
        Z2 = _RTD_A * _RTD_A - (4 * _RTD_B)
 | 
			
		||||
        Z3 = (4 * _RTD_B) / self.rtd_nominal
 | 
			
		||||
        Z4 = 2 * _RTD_B
 | 
			
		||||
        temp = Z2 + (Z3 * raw_reading)
 | 
			
		||||
        temp = (math.sqrt(temp) + Z1) / Z4
 | 
			
		||||
        if temp >= 0:
 | 
			
		||||
            return temp
 | 
			
		||||
 | 
			
		||||
        # For the following math to work, nominal RTD resistance must be normalized to 100 ohms
 | 
			
		||||
        raw_reading /= self.rtd_nominal
 | 
			
		||||
        raw_reading *= 100
 | 
			
		||||
 | 
			
		||||
        rpoly = raw_reading
 | 
			
		||||
        temp = -242.02
 | 
			
		||||
        temp += 2.2228 * rpoly
 | 
			
		||||
        rpoly *= raw_reading  # square
 | 
			
		||||
        temp += 2.5859e-3 * rpoly
 | 
			
		||||
        rpoly *= raw_reading  # ^3
 | 
			
		||||
        temp -= 4.8260e-6 * rpoly
 | 
			
		||||
        rpoly *= raw_reading  # ^4
 | 
			
		||||
        temp -= 2.8183e-8 * rpoly
 | 
			
		||||
        rpoly *= raw_reading  # ^5
 | 
			
		||||
        temp += 1.5243e-10 * rpoly
 | 
			
		||||
        return temp
 | 
			
		||||
							
								
								
									
										559
									
								
								station-esp8266/lib-src/bme280_i2c.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										559
									
								
								station-esp8266/lib-src/bme280_i2c.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,559 @@
 | 
			
		||||
# Author(s): Jonathan Hanson 2018
 | 
			
		||||
#
 | 
			
		||||
# This is more or less a straight read of the Bosch data sheet at:
 | 
			
		||||
# https://www.bosch-sensortec.com/bst/products/all_products/bme280
 | 
			
		||||
# and specifically:
 | 
			
		||||
# https://ae-bst.resource.bosch.com/media/_tech/media/datasheets/BST-BME280_DS001-12.pdf
 | 
			
		||||
#
 | 
			
		||||
# Modeled on the reference library Bosch Sensortec C library at:
 | 
			
		||||
# https://github.com/BoschSensortec/BME280_driver
 | 
			
		||||
#
 | 
			
		||||
# The development of this module was heavily guided by the prior work done
 | 
			
		||||
# by Peter Dahlberg et al at:
 | 
			
		||||
# https://github.com/catdog2/mpy_bme280_esp8266
 | 
			
		||||
 | 
			
		||||
from micropython import const
 | 
			
		||||
from ustruct import unpack, unpack_from
 | 
			
		||||
from utime import sleep_ms
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
# BME280 default address
 | 
			
		||||
BME280_I2C_ADDR_PRIM                  = const(0x76)
 | 
			
		||||
BME280_I2C_ADDR_SEC                   = const(0x77)
 | 
			
		||||
 | 
			
		||||
# Sensor Power Mode Options
 | 
			
		||||
BME280_SLEEP_MODE                     = const(0x00)
 | 
			
		||||
BME280_FORCED_MODE                    = const(0x01)
 | 
			
		||||
BME280_NORMAL_MODE                    = const(0x03)
 | 
			
		||||
 | 
			
		||||
# Oversampling Options
 | 
			
		||||
BME280_NO_OVERSAMPLING                = const(0x00)
 | 
			
		||||
BME280_OVERSAMPLING_1X                = const(0x01)
 | 
			
		||||
BME280_OVERSAMPLING_2X                = const(0x02)
 | 
			
		||||
BME280_OVERSAMPLING_4X                = const(0x03)
 | 
			
		||||
BME280_OVERSAMPLING_8X                = const(0x04)
 | 
			
		||||
BME280_OVERSAMPLING_16X               = const(0x05)
 | 
			
		||||
 | 
			
		||||
# Standby Duration Options
 | 
			
		||||
BME280_STANDBY_TIME_500_US            = const(0x00)  # Note this is microseconds, so 0.5 ms
 | 
			
		||||
BME280_STANDBY_TIME_62_5_MS           = const(0x01)
 | 
			
		||||
BME280_STANDBY_TIME_125_MS            = const(0x02)
 | 
			
		||||
BME280_STANDBY_TIME_250_MS            = const(0x03)
 | 
			
		||||
BME280_STANDBY_TIME_500_MS            = const(0x04)
 | 
			
		||||
BME280_STANDBY_TIME_1000_MS           = const(0x05)
 | 
			
		||||
BME280_STANDBY_TIME_10_MS             = const(0x06)
 | 
			
		||||
BME280_STANDBY_TIME_20_MS             = const(0x07)
 | 
			
		||||
 | 
			
		||||
# Filter Coefficient Options
 | 
			
		||||
BME280_FILTER_COEFF_OFF               = const(0x00)
 | 
			
		||||
BME280_FILTER_COEFF_2                 = const(0x01)
 | 
			
		||||
BME280_FILTER_COEFF_4                 = const(0x02)
 | 
			
		||||
BME280_FILTER_COEFF_8                 = const(0x03)
 | 
			
		||||
BME280_FILTER_COEFF_16                = const(0x04)
 | 
			
		||||
 | 
			
		||||
# BME280 Chip ID
 | 
			
		||||
_BME280_CHIP_ID                       = const(0x60)
 | 
			
		||||
 | 
			
		||||
# Register Addresses
 | 
			
		||||
_BME280_CHIP_ID_ADDR                  = const(0xD0)
 | 
			
		||||
_BME280_RESET_ADDR                    = const(0xE0)
 | 
			
		||||
_BME280_TEMP_PRESS_CALIB_DATA_ADDR    = const(0x88)
 | 
			
		||||
_BME280_HUMIDITY_CALIB_DATA_ADDR      = const(0xE1)
 | 
			
		||||
_BME280_PWR_CTRL_ADDR                 = const(0xF4)
 | 
			
		||||
_BME280_CTRL_HUM_ADDR                 = const(0xF2)
 | 
			
		||||
_BME280_CTRL_MEAS_ADDR                = const(0xF4)
 | 
			
		||||
_BME280_CONFIG_ADDR                   = const(0xF5)
 | 
			
		||||
_BME280_DATA_ADDR                     = const(0xF7)
 | 
			
		||||
 | 
			
		||||
# Register range sizes
 | 
			
		||||
_BME280_TEMP_PRESS_CALIB_DATA_LEN     = const(26)
 | 
			
		||||
_BME280_HUMIDITY_CALIB_DATA_LEN       = const(7)
 | 
			
		||||
_BME280_P_T_H_DATA_LEN                = const(8)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class BME280_I2C:
 | 
			
		||||
    def __init__(self, address: int = BME280_I2C_ADDR_PRIM, i2c=None):
 | 
			
		||||
        """
 | 
			
		||||
        Ensure I2C communication with the sensor is working, reset the sensor,
 | 
			
		||||
        and load its calibration data into memory.
 | 
			
		||||
        """
 | 
			
		||||
        self.address = address
 | 
			
		||||
 | 
			
		||||
        if i2c is None:
 | 
			
		||||
            raise ValueError('A configured I2C object is required.')
 | 
			
		||||
        self.i2c = i2c
 | 
			
		||||
 | 
			
		||||
        self._read_chip_id()
 | 
			
		||||
        self._soft_reset()
 | 
			
		||||
        self._load_calibration_data()
 | 
			
		||||
 | 
			
		||||
    def _read_chip_id(self):
 | 
			
		||||
        """
 | 
			
		||||
        Read the chip ID from the sensor and verify it's correct.
 | 
			
		||||
        If the value isn't correct, wait 1ms and try again.
 | 
			
		||||
        If 5 tries don't work, raise an exception.
 | 
			
		||||
        """
 | 
			
		||||
        for x in range(5):
 | 
			
		||||
            mem = self.i2c.readfrom_mem(self.address, _BME280_CHIP_ID_ADDR, 1)
 | 
			
		||||
            if mem[0] == _BME280_CHIP_ID:
 | 
			
		||||
                return
 | 
			
		||||
            sleep_ms(1)
 | 
			
		||||
        raise Exception("Couldn't read BME280 chip ID after 5 attempts.")
 | 
			
		||||
 | 
			
		||||
    def _soft_reset(self):
 | 
			
		||||
        """
 | 
			
		||||
        Write the reset command to the sensor's reset address.
 | 
			
		||||
        Wait 2ms, per the reference library's example.
 | 
			
		||||
        """
 | 
			
		||||
        self.i2c.writeto_mem(self.address, _BME280_RESET_ADDR, bytearray([0xB6]))
 | 
			
		||||
        sleep_ms(2)
 | 
			
		||||
 | 
			
		||||
    def _load_calibration_data(self):
 | 
			
		||||
        """
 | 
			
		||||
        Load the read-only calibration values out of the sensor's memory, to be
 | 
			
		||||
        used later in calibrating the raw reads.  These get stored in various
 | 
			
		||||
        self.cal_dig_* object properties.
 | 
			
		||||
 | 
			
		||||
        See https://github.com/BoschSensortec/BME280_driver/blob/bme280_v3.3.4/bme280.c#L1192
 | 
			
		||||
        See https://github.com/BoschSensortec/BME280_driver/blob/bme280_v3.3.4/bme280.c#L1216
 | 
			
		||||
        See https://github.com/catdog2/mpy_bme280_esp8266/blob/master/bme280.py#L73
 | 
			
		||||
        """
 | 
			
		||||
        # Load the temperature and pressure calibration data
 | 
			
		||||
        # (note that the first value of the humidity data is stuffed in here)
 | 
			
		||||
        tp_cal_mem = self.i2c.readfrom_mem(self.address,
 | 
			
		||||
                                           _BME280_TEMP_PRESS_CALIB_DATA_ADDR,
 | 
			
		||||
                                           _BME280_TEMP_PRESS_CALIB_DATA_LEN)
 | 
			
		||||
 | 
			
		||||
        (self.cal_dig_T1, self.cal_dig_T2, self.cal_dig_T3,
 | 
			
		||||
            self.cal_dig_P1, self.cal_dig_P2, self.cal_dig_P3,
 | 
			
		||||
            self.cal_dig_P4, self.cal_dig_P5, self.cal_dig_P6,
 | 
			
		||||
            self.cal_dig_P7, self.cal_dig_P8, self.cal_dig_P9,
 | 
			
		||||
            _,
 | 
			
		||||
            self.cal_dig_H1) = unpack("<HhhHhhhhhhhhBB", tp_cal_mem)
 | 
			
		||||
 | 
			
		||||
        # Load the rest of the humidity calibration data
 | 
			
		||||
        hum_cal_mem = self.i2c.readfrom_mem(self.address,
 | 
			
		||||
                                            _BME280_HUMIDITY_CALIB_DATA_ADDR,
 | 
			
		||||
                                            _BME280_HUMIDITY_CALIB_DATA_LEN)
 | 
			
		||||
 | 
			
		||||
        self.cal_dig_H2, self.cal_dig_H3 = unpack("<hB", hum_cal_mem)
 | 
			
		||||
 | 
			
		||||
        e4_sign = unpack_from("<b", hum_cal_mem, 3)[0]
 | 
			
		||||
        self.cal_dig_H4 = (e4_sign << 4) | (hum_cal_mem[4] & 0b00001111)
 | 
			
		||||
 | 
			
		||||
        e6_sign = unpack_from("<b", hum_cal_mem, 5)[0]
 | 
			
		||||
        self.cal_dig_H5 = (e6_sign << 4) | (hum_cal_mem[4] >> 4)
 | 
			
		||||
 | 
			
		||||
        self.cal_dig_H6 = unpack_from("<b", hum_cal_mem, 6)[0]
 | 
			
		||||
 | 
			
		||||
        # Initialize the cal_t_fine carry-over value used during compensation
 | 
			
		||||
        self.cal_t_fine = 0
 | 
			
		||||
 | 
			
		||||
    def get_measurement_settings(self):
 | 
			
		||||
        """
 | 
			
		||||
        Return a parsed set of the sensor's measurement settings as a dict
 | 
			
		||||
        These values include oversampling settings for each measurement,
 | 
			
		||||
        the IIR filter coefficient, and the standby duration for normal
 | 
			
		||||
        power mode.
 | 
			
		||||
        See the data sheet, section 3 and 5
 | 
			
		||||
        """
 | 
			
		||||
        mem = self.i2c.readfrom_mem(self.address, _BME280_CTRL_HUM_ADDR, 4)
 | 
			
		||||
        ctrl_hum, _, ctrl_meas, config = unpack("<BBBB", mem)
 | 
			
		||||
 | 
			
		||||
        return {
 | 
			
		||||
            "osr_h":        (ctrl_hum & 0b00000111),
 | 
			
		||||
            "osr_p":        (ctrl_meas >> 2) & 0b00000111,
 | 
			
		||||
            "osr_t":        (ctrl_meas >> 5) & 0b00000111,
 | 
			
		||||
            "filter":       (config >> 2) & 0b00000111,
 | 
			
		||||
            "standby_time": (config >> 5) & 0b00000111,
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    def set_measurement_settings(self, settings: dict):
 | 
			
		||||
        """
 | 
			
		||||
        Set the sensor's settings for each measurement's oversampling,
 | 
			
		||||
        the pressure IIR filter coefficient, and standby duration
 | 
			
		||||
        during normal power mode.
 | 
			
		||||
 | 
			
		||||
        The settings dict can have keys osr_h, osr_p, osr_t, filter, and
 | 
			
		||||
        standby_time.  All values are optional, and omitting any will retain
 | 
			
		||||
        the pre-existing value.
 | 
			
		||||
 | 
			
		||||
        See the data sheet, section 3 and 5
 | 
			
		||||
        """
 | 
			
		||||
        self._validate_settings(settings)
 | 
			
		||||
        self._ensure_sensor_is_asleep()
 | 
			
		||||
        self._write_measurement_settings(settings)
 | 
			
		||||
 | 
			
		||||
    def _validate_settings(self, settings: dict):
 | 
			
		||||
        oversampling_options = [
 | 
			
		||||
            BME280_NO_OVERSAMPLING, BME280_OVERSAMPLING_1X,
 | 
			
		||||
            BME280_OVERSAMPLING_2X, BME280_OVERSAMPLING_4X,
 | 
			
		||||
            BME280_OVERSAMPLING_8X, BME280_OVERSAMPLING_16X]
 | 
			
		||||
 | 
			
		||||
        filter_options = [
 | 
			
		||||
            BME280_FILTER_COEFF_OFF, BME280_FILTER_COEFF_2,
 | 
			
		||||
            BME280_FILTER_COEFF_4, BME280_FILTER_COEFF_8,
 | 
			
		||||
            BME280_FILTER_COEFF_16]
 | 
			
		||||
 | 
			
		||||
        standby_time_options = [
 | 
			
		||||
            BME280_STANDBY_TIME_500_US,
 | 
			
		||||
            BME280_STANDBY_TIME_62_5_MS, BME280_STANDBY_TIME_125_MS,
 | 
			
		||||
            BME280_STANDBY_TIME_250_MS, BME280_STANDBY_TIME_500_MS,
 | 
			
		||||
            BME280_STANDBY_TIME_1000_MS, BME280_STANDBY_TIME_10_MS,
 | 
			
		||||
            BME280_STANDBY_TIME_20_MS]
 | 
			
		||||
 | 
			
		||||
        if 'osr_h' in settings:
 | 
			
		||||
            if settings['osr_h'] not in oversampling_options:
 | 
			
		||||
                raise ValueError("osr_h must be one of the oversampling defines")
 | 
			
		||||
        if 'osr_p' in settings:
 | 
			
		||||
            if settings['osr_h'] not in oversampling_options:
 | 
			
		||||
                raise ValueError("osr_p must be one of the oversampling defines")
 | 
			
		||||
        if 'osr_t' in settings:
 | 
			
		||||
            if settings['osr_h'] not in oversampling_options:
 | 
			
		||||
                raise ValueError("osr_t must be one of the oversampling defines")
 | 
			
		||||
        if 'filter' in settings:
 | 
			
		||||
            if settings['filter'] not in filter_options:
 | 
			
		||||
                raise ValueError("filter filter coefficient defines")
 | 
			
		||||
        if 'standby_time' in settings:
 | 
			
		||||
            if settings['standby_time'] not in standby_time_options:
 | 
			
		||||
                raise ValueError("standby_time must be one of the standby time duration defines")
 | 
			
		||||
 | 
			
		||||
    def _write_measurement_settings(self, settings: dict):
 | 
			
		||||
        # Read in the existing configuration, to modify
 | 
			
		||||
        mem = self.i2c.readfrom_mem(self.address, _BME280_CTRL_HUM_ADDR, 4)
 | 
			
		||||
        ctrl_hum, _, ctrl_meas, config = unpack("<BBBB", mem)
 | 
			
		||||
 | 
			
		||||
        # Make any changes necessary to the ctrl_hum register
 | 
			
		||||
        if "osr_h" in settings:
 | 
			
		||||
            newval = (ctrl_hum & 0b11111000) | (settings['osr_h'] & 0b00000111)
 | 
			
		||||
            self.i2c.writeto_mem(self.address, _BME280_CTRL_HUM_ADDR, bytearray([newval]))
 | 
			
		||||
 | 
			
		||||
            # according to the data sheet, ctrl_hum needs a write to
 | 
			
		||||
            # ctrl_meas in order to take effect
 | 
			
		||||
            self.i2c.writeto_mem(self.address, _BME280_CTRL_MEAS_ADDR, bytearray([ctrl_meas]))
 | 
			
		||||
 | 
			
		||||
        # Make any changes necessary to the ctrl_meas register
 | 
			
		||||
        if "osr_p" in settings or "osr_t" in settings:
 | 
			
		||||
            newval = ctrl_meas
 | 
			
		||||
            if "osr_p" in settings:
 | 
			
		||||
                newval = (newval & 0b11100011) | ((settings['osr_p'] << 2) & 0b00011100)
 | 
			
		||||
            if "osr_t" in settings:
 | 
			
		||||
                newval = (newval & 0b00011111) | ((settings['osr_t'] << 5) & 0b11100000)
 | 
			
		||||
            self.i2c.writeto_mem(self.address, _BME280_CTRL_MEAS_ADDR, bytearray([newval]))
 | 
			
		||||
 | 
			
		||||
        # Make any changes necessary to the config register
 | 
			
		||||
        if "filter" in settings or "standby_time" in settings:
 | 
			
		||||
            newval = config
 | 
			
		||||
            if "filter" in settings:
 | 
			
		||||
                newval = (newval & 0b11100011) | ((settings['filter'] << 2) & 0b00011100)
 | 
			
		||||
            if "standby_time" in settings:
 | 
			
		||||
                newval = (newval & 0b00011111) | ((settings['standby_time'] << 5) & 0b11100000)
 | 
			
		||||
            self.i2c.writeto_mem(self.address, _BME280_CONFIG_ADDR, bytearray([newval]))
 | 
			
		||||
 | 
			
		||||
    def get_power_mode(self):
 | 
			
		||||
        """
 | 
			
		||||
        Result will be one of BME280_SLEEP_MODE, BME280_FORCED_MODE, or
 | 
			
		||||
        BME280_NORMAL_MODE.
 | 
			
		||||
        See the data sheet, section 3.3
 | 
			
		||||
        """
 | 
			
		||||
        mem = self.i2c.readfrom_mem(self.address, _BME280_PWR_CTRL_ADDR, 1)
 | 
			
		||||
        return (mem[0] & 0b00000011)
 | 
			
		||||
 | 
			
		||||
    def set_power_mode(self, new_power_mode: int):
 | 
			
		||||
        """
 | 
			
		||||
        Configure the sensor's power mode (BME280_SLEEP_MODE,
 | 
			
		||||
        BME280_FORCED_MODE, or BME280_NORMAL_MODE)
 | 
			
		||||
 | 
			
		||||
        Note that setting to forced mode will immediately set the sensor back
 | 
			
		||||
        to sleep mode after taking a measurement.
 | 
			
		||||
 | 
			
		||||
        See the data sheet, section 3.3
 | 
			
		||||
        """
 | 
			
		||||
        if new_power_mode not in [BME280_SLEEP_MODE, BME280_FORCED_MODE, BME280_NORMAL_MODE]:
 | 
			
		||||
            raise ValueError("New power mode must be sleep, forced, or normal constant")
 | 
			
		||||
 | 
			
		||||
        self._ensure_sensor_is_asleep()
 | 
			
		||||
 | 
			
		||||
        # Read the current register, mask out and set the new power mode,
 | 
			
		||||
        # and write the register back to the device.
 | 
			
		||||
        mem = self.i2c.readfrom_mem(self.address, _BME280_PWR_CTRL_ADDR, 1)
 | 
			
		||||
        newval = (mem[0] & 0b11111100) | (new_power_mode & 0b00000011)
 | 
			
		||||
        self.i2c.writeto_mem(self.address, _BME280_PWR_CTRL_ADDR, bytearray([newval]))
 | 
			
		||||
 | 
			
		||||
    def _ensure_sensor_is_asleep(self):
 | 
			
		||||
        """
 | 
			
		||||
        If the sensor mode isn't already "sleep", then put it to sleep.
 | 
			
		||||
 | 
			
		||||
        This is done by reading out the configuration values we want to keep,
 | 
			
		||||
        and then doing a soft reset and writing those values back.
 | 
			
		||||
        """
 | 
			
		||||
        if self.get_power_mode() != BME280_SLEEP_MODE:
 | 
			
		||||
            settings = self.get_measurement_settings()
 | 
			
		||||
            self._soft_reset()
 | 
			
		||||
            self._write_measurement_settings(settings)
 | 
			
		||||
 | 
			
		||||
    def get_measurement(self):
 | 
			
		||||
        """
 | 
			
		||||
        Return a set of measurements in decimal value, compensated with the
 | 
			
		||||
        sensor's stored calibration data.
 | 
			
		||||
        """
 | 
			
		||||
        uncompensated_data = self._read_uncompensated_data()
 | 
			
		||||
 | 
			
		||||
        # Be sure to call self._compensate_temperature() first, as it sets a
 | 
			
		||||
        # global "fine" calibration value for the other two compensation
 | 
			
		||||
        # functions
 | 
			
		||||
        return {
 | 
			
		||||
            "temperature": self._compensate_temperature(uncompensated_data['temperature']),
 | 
			
		||||
            "pressure":    self._compensate_pressure(uncompensated_data['pressure']),
 | 
			
		||||
            "humidity":    self._compensate_humidity(uncompensated_data['humidity']),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    def _read_uncompensated_data(self):
 | 
			
		||||
        # Read the uncompensated temperature, pressure, and humidity data
 | 
			
		||||
        mem = self.i2c.readfrom_mem(self.address, _BME280_DATA_ADDR, _BME280_P_T_H_DATA_LEN)
 | 
			
		||||
        (press_msb, press_lsb, press_xlsb,
 | 
			
		||||
            temp_msb, temp_lsb, temp_xlsb,
 | 
			
		||||
            hum_msb, hum_lsb) = unpack("<BBBBBBBB", mem)
 | 
			
		||||
 | 
			
		||||
        # Assemble the values from the memory fragments and return a dict.
 | 
			
		||||
        #
 | 
			
		||||
        # Note that we're calling temperature first, since it sets the
 | 
			
		||||
        # cal_t_fine value used in humidity and pressure.
 | 
			
		||||
        return {
 | 
			
		||||
            "temperature": (temp_msb << 12) | (temp_lsb << 4) | (temp_xlsb >> 4),
 | 
			
		||||
            "pressure":    (press_msb << 12) | (press_lsb << 4) | (press_xlsb >> 4),
 | 
			
		||||
            "humidity":    (hum_msb << 8) | (hum_lsb),
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    ##
 | 
			
		||||
    # Float Implementations
 | 
			
		||||
    ##
 | 
			
		||||
 | 
			
		||||
    # def _compensate_temperature(self, adc_T: int) -> float:
 | 
			
		||||
    #     """
 | 
			
		||||
    #     Output value of “25.0” equals 25.0 DegC.
 | 
			
		||||
    #
 | 
			
		||||
    #     See the floating-point implementation in the reference library:
 | 
			
		||||
    #     https://github.com/BoschSensortec/BME280_driver/blob/bme280_v3.3.4/bme280.c#L884
 | 
			
		||||
    #     """
 | 
			
		||||
    #     temperature_min = -40
 | 
			
		||||
    #     temperature_max = 85
 | 
			
		||||
    #
 | 
			
		||||
    #     var1 = (adc_T / 16384.0) - (self.cal_dig_T1 / 1024.0)
 | 
			
		||||
    #     var1 = var1 * self.cal_dig_T2
 | 
			
		||||
    #
 | 
			
		||||
    #     var2 = (adc_T / 131072.0) - (self.cal_dig_T1 / 8192.0)
 | 
			
		||||
    #     var2 = var2 * var2 * self.cal_dig_T3
 | 
			
		||||
    #
 | 
			
		||||
    #     self.cal_t_fine = int(var1 + var2)
 | 
			
		||||
    #
 | 
			
		||||
    #     temperature = (var1 + var2) / 5120.0
 | 
			
		||||
    #
 | 
			
		||||
    #     if temperature < temperature_min:
 | 
			
		||||
    #         temperature = temperature_min
 | 
			
		||||
    #     elif temperature > temperature_max:
 | 
			
		||||
    #         temperature = temperature_max
 | 
			
		||||
    #
 | 
			
		||||
    #     return temperature
 | 
			
		||||
 | 
			
		||||
    # def _compensate_pressure(self, adc_P: int) -> float:
 | 
			
		||||
    #     """
 | 
			
		||||
    #     Output value of “96386.0” equals 96386 Pa = 963.86 hPa
 | 
			
		||||
    #
 | 
			
		||||
    #     See the floating-point implementation in the reference library:
 | 
			
		||||
    #     https://github.com/BoschSensortec/BME280_driver/blob/bme280_v3.3.4/bme280.c#L912
 | 
			
		||||
    #     """
 | 
			
		||||
    #     pressure_min = 30000.0
 | 
			
		||||
    #     pressure_max = 110000.0
 | 
			
		||||
    #
 | 
			
		||||
    #     var1 = (self.cal_t_fine / 2.0) - 64000.0
 | 
			
		||||
    #
 | 
			
		||||
    #     var2 = var1 * var1 * self.cal_dig_P6 / 32768.0
 | 
			
		||||
    #     var2 = var2 + (var1 * self.cal_dig_P5 * 2.0)
 | 
			
		||||
    #     var2 = (var2 / 4.0) + (self.cal_dig_P4 * 65536.0)
 | 
			
		||||
    #
 | 
			
		||||
    #     var3 = self.cal_dig_P3 * var1 * var1 / 524288.0
 | 
			
		||||
    #
 | 
			
		||||
    #     var1 = (var3 + self.cal_dig_P2 * var1) / 524288.0
 | 
			
		||||
    #     var1 = (1.0 + var1 / 32768.0) * self.cal_dig_P1
 | 
			
		||||
    #
 | 
			
		||||
    #     # avoid exception caused by division by zero
 | 
			
		||||
    #     if var1:
 | 
			
		||||
    #         pressure = 1048576.0 - adc_P
 | 
			
		||||
    #         pressure = (pressure - (var2 / 4096.0)) * 6250.0 / var1
 | 
			
		||||
    #         var1 = self.cal_dig_P9 * pressure * pressure / 2147483648.0
 | 
			
		||||
    #         var2 = pressure * self.cal_dig_P8 / 32768.0
 | 
			
		||||
    #         pressure = pressure + (var1 + var2 + self.cal_dig_P7) / 16.0
 | 
			
		||||
    #
 | 
			
		||||
    #         if pressure < pressure_min:
 | 
			
		||||
    #             pressure = pressure_min
 | 
			
		||||
    #         elif pressure > pressure_max:
 | 
			
		||||
    #             pressure = pressure_max
 | 
			
		||||
    #
 | 
			
		||||
    #     else:
 | 
			
		||||
    #         # Invalid case
 | 
			
		||||
    #         pressure = pressure_min
 | 
			
		||||
    #
 | 
			
		||||
    #     return pressure
 | 
			
		||||
 | 
			
		||||
    # def _compensate_humidity(self, adc_H: int) -> float:
 | 
			
		||||
    #     """
 | 
			
		||||
    #     Output value between 0.0 and 100.0, where 100.0 is 100%RH
 | 
			
		||||
    #
 | 
			
		||||
    #     See the floating-point implementation in the reference library:
 | 
			
		||||
    #     https://github.com/BoschSensortec/BME280_driver/blob/bme280_v3.3.4/bme280.c#L952
 | 
			
		||||
    #     """
 | 
			
		||||
    #     humidity_min = 0.0
 | 
			
		||||
    #     humidity_max = 100.0
 | 
			
		||||
    #
 | 
			
		||||
    #     var1 = self.cal_t_fine - 76800.0
 | 
			
		||||
    #
 | 
			
		||||
    #     var2 = self.cal_dig_H4 * 64.0 + (self.cal_dig_H5 / 16384.0) * var1
 | 
			
		||||
    #
 | 
			
		||||
    #     var3 = adc_H - var2
 | 
			
		||||
    #
 | 
			
		||||
    #     var4 = self.cal_dig_H2 / 65536.0
 | 
			
		||||
    #
 | 
			
		||||
    #     var5 = 1.0 + (self.cal_dig_H3 / 67108864.0) * var1
 | 
			
		||||
    #
 | 
			
		||||
    #     var6 = 1.0 + (self.cal_dig_H6 / 67108864.0) * var1 * var5
 | 
			
		||||
    #     var6 = var3 * var4 * (var5 * var6)
 | 
			
		||||
    #
 | 
			
		||||
    #     humidity = var6 * (1.0 - self.cal_dig_H1 * var6 / 524288.0)
 | 
			
		||||
    #
 | 
			
		||||
    #     if humidity > humidity_max:
 | 
			
		||||
    #         humidity = humidity_max
 | 
			
		||||
    #     elif humidity < humidity_min:
 | 
			
		||||
    #         humidity = humidity_min
 | 
			
		||||
    #
 | 
			
		||||
    #     return humidity
 | 
			
		||||
 | 
			
		||||
    ##
 | 
			
		||||
    # 32-Bit Integer Implementations
 | 
			
		||||
    ##
 | 
			
		||||
 | 
			
		||||
    def _compensate_temperature(self, adc_T: int) -> float:
 | 
			
		||||
        """
 | 
			
		||||
        Output value of “25.0” equals 25.0 DegC.
 | 
			
		||||
 | 
			
		||||
        See the integer implementation in the data sheet, section 4.2.3
 | 
			
		||||
        And the reference library:
 | 
			
		||||
        https://github.com/BoschSensortec/BME280_driver/blob/bme280_v3.3.4/bme280.c#L987
 | 
			
		||||
        """
 | 
			
		||||
        temperature_min = -4000
 | 
			
		||||
        temperature_max = 8500
 | 
			
		||||
 | 
			
		||||
        var1 = (((adc_T // 8) - (self.cal_dig_T1 * 2)) * self.cal_dig_T2) // 2048
 | 
			
		||||
 | 
			
		||||
        var2 = (((((adc_T // 16) - self.cal_dig_T1) * ((adc_T // 16) - self.cal_dig_T1)) // 4096) * self.cal_dig_T3) // 16384
 | 
			
		||||
 | 
			
		||||
        self.cal_t_fine = var1 + var2
 | 
			
		||||
 | 
			
		||||
        temperature = (self.cal_t_fine * 5 + 128) // 256
 | 
			
		||||
 | 
			
		||||
        if temperature < temperature_min:
 | 
			
		||||
            temperature = temperature_min
 | 
			
		||||
        elif temperature > temperature_max:
 | 
			
		||||
            temperature = temperature_max
 | 
			
		||||
 | 
			
		||||
        return temperature / 100
 | 
			
		||||
 | 
			
		||||
    def _compensate_pressure(self, adc_P: int) -> float:
 | 
			
		||||
        """
 | 
			
		||||
        Output value of “96386.0” equals 96386 Pa = 963.86 hPa
 | 
			
		||||
 | 
			
		||||
        See the 32-bit integer implementation in the data sheet, section 4.2.3
 | 
			
		||||
        And the reference library:
 | 
			
		||||
        https://github.com/BoschSensortec/BME280_driver/blob/bme280_v3.3.4/bme280.c#L1059
 | 
			
		||||
 | 
			
		||||
        Note that there's a 64-bit version of this function in the reference
 | 
			
		||||
        library on line 1016 that we're leaving unimplemented.
 | 
			
		||||
        """
 | 
			
		||||
        pressure_min = 30000
 | 
			
		||||
        pressure_max = 110000
 | 
			
		||||
 | 
			
		||||
        var1 = (self.cal_t_fine // 2) - 64000
 | 
			
		||||
 | 
			
		||||
        var2 = (((var1 // 4) * (var1 // 4)) // 2048) * self.cal_dig_P6
 | 
			
		||||
        var2 = var2 + ((var1 * self.cal_dig_P5) * 2)
 | 
			
		||||
        var2 = (var2 // 4) + (self.cal_dig_P4 * 65536)
 | 
			
		||||
 | 
			
		||||
        var3 = (self.cal_dig_P3 * (((var1 // 4) * (var1 // 4)) // 8192)) // 8
 | 
			
		||||
 | 
			
		||||
        var4 = (self.cal_dig_P2 * var1) // 2
 | 
			
		||||
 | 
			
		||||
        var1 = (var3 + var4) // 262144
 | 
			
		||||
        var1 = ((32768 + var1) * self.cal_dig_P1) // 32768
 | 
			
		||||
 | 
			
		||||
        # avoid exception caused by division by zero
 | 
			
		||||
        if var1:
 | 
			
		||||
            var5 = 1048576 - adc_P
 | 
			
		||||
 | 
			
		||||
            pressure = (var5 - (var2 // 4096)) * 3125
 | 
			
		||||
 | 
			
		||||
            if pressure < 0x80000000:
 | 
			
		||||
                pressure = (pressure << 1) // var1
 | 
			
		||||
            else:
 | 
			
		||||
                pressure = (pressure // var1) * 2
 | 
			
		||||
 | 
			
		||||
            var1 = (self.cal_dig_P9 * (((pressure // 8) * (pressure // 8)) // 8192)) // 4096
 | 
			
		||||
 | 
			
		||||
            var2 = (((pressure // 4)) * self.cal_dig_P8) // 8192
 | 
			
		||||
 | 
			
		||||
            pressure = pressure + ((var1 + var2 + self.cal_dig_P7) // 16)
 | 
			
		||||
 | 
			
		||||
            if pressure < pressure_min:
 | 
			
		||||
                pressure = pressure_min
 | 
			
		||||
            elif pressure > pressure_max:
 | 
			
		||||
                pressure = pressure_max
 | 
			
		||||
 | 
			
		||||
        else:
 | 
			
		||||
            # Invalid case
 | 
			
		||||
            pressure = pressure_min
 | 
			
		||||
 | 
			
		||||
        return pressure
 | 
			
		||||
 | 
			
		||||
    def _compensate_humidity(self, adc_H: int) -> float:
 | 
			
		||||
        """
 | 
			
		||||
        Output value between 0.0 and 100.0, where 100.0 is 100%RH
 | 
			
		||||
 | 
			
		||||
        See the floating-point implementation in the reference library:
 | 
			
		||||
        https://github.com/BoschSensortec/BME280_driver/blob/bme280_v3.3.4/bme280.c#1108
 | 
			
		||||
        """
 | 
			
		||||
 | 
			
		||||
        humidity_max = 102400
 | 
			
		||||
 | 
			
		||||
        var1 = self.cal_t_fine - 76800
 | 
			
		||||
 | 
			
		||||
        var2 = adc_H * 16384
 | 
			
		||||
 | 
			
		||||
        var3 = self.cal_dig_H4 * 1048576
 | 
			
		||||
 | 
			
		||||
        var4 = self.cal_dig_H5 * var1
 | 
			
		||||
 | 
			
		||||
        var5 = (((var2 - var3) - var4) + 16384) // 32768
 | 
			
		||||
 | 
			
		||||
        var2 = (var1 * self.cal_dig_H6) // 1024
 | 
			
		||||
 | 
			
		||||
        var3 = (var1 * self.cal_dig_H3) // 2048
 | 
			
		||||
 | 
			
		||||
        var4 = ((var2 * (var3 + 32768)) // 1024) + 2097152
 | 
			
		||||
 | 
			
		||||
        var2 = ((var4 * self.cal_dig_H2) + 8192) // 16384
 | 
			
		||||
 | 
			
		||||
        var3 = var5 * var2
 | 
			
		||||
 | 
			
		||||
        var4 = ((var3 // 32768) * (var3 // 32768)) // 128
 | 
			
		||||
 | 
			
		||||
        var5 = var3 - ((var4 * self.cal_dig_H1) // 16)
 | 
			
		||||
        if var5 < 0:
 | 
			
		||||
            var5 = 0
 | 
			
		||||
        if var5 > 419430400:
 | 
			
		||||
            var5 = 419430400
 | 
			
		||||
 | 
			
		||||
        humidity = var5 // 4096
 | 
			
		||||
 | 
			
		||||
        if (humidity > humidity_max):
 | 
			
		||||
            humidity = humidity_max
 | 
			
		||||
 | 
			
		||||
        return humidity / 1024
 | 
			
		||||
							
								
								
									
										44
									
								
								station-esp8266/lib-src/umqtt/robust.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								station-esp8266/lib-src/umqtt/robust.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,44 @@
 | 
			
		||||
import time
 | 
			
		||||
from . import simple
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class MQTTClient(simple.MQTTClient):
 | 
			
		||||
 | 
			
		||||
    DELAY = 2
 | 
			
		||||
    DEBUG = False
 | 
			
		||||
 | 
			
		||||
    def delay(self, i):
 | 
			
		||||
        time.sleep(self.DELAY)
 | 
			
		||||
 | 
			
		||||
    def log(self, in_reconnect, e):
 | 
			
		||||
        if self.DEBUG:
 | 
			
		||||
            if in_reconnect:
 | 
			
		||||
                print("mqtt reconnect: %r" % e)
 | 
			
		||||
            else:
 | 
			
		||||
                print("mqtt: %r" % e)
 | 
			
		||||
 | 
			
		||||
    def reconnect(self):
 | 
			
		||||
        i = 0
 | 
			
		||||
        while 1:
 | 
			
		||||
            try:
 | 
			
		||||
                return super().connect(False)
 | 
			
		||||
            except OSError as e:
 | 
			
		||||
                self.log(True, e)
 | 
			
		||||
                i += 1
 | 
			
		||||
                self.delay(i)
 | 
			
		||||
 | 
			
		||||
    def publish(self, topic, msg, retain=False, qos=0):
 | 
			
		||||
        while 1:
 | 
			
		||||
            try:
 | 
			
		||||
                return super().publish(topic, msg, retain, qos)
 | 
			
		||||
            except OSError as e:
 | 
			
		||||
                self.log(False, e)
 | 
			
		||||
            self.reconnect()
 | 
			
		||||
 | 
			
		||||
    def wait_msg(self):
 | 
			
		||||
        while 1:
 | 
			
		||||
            try:
 | 
			
		||||
                return super().wait_msg()
 | 
			
		||||
            except OSError as e:
 | 
			
		||||
                self.log(False, e)
 | 
			
		||||
            self.reconnect()
 | 
			
		||||
							
								
								
									
										204
									
								
								station-esp8266/lib-src/umqtt/simple.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										204
									
								
								station-esp8266/lib-src/umqtt/simple.py
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,204 @@
 | 
			
		||||
import usocket as socket
 | 
			
		||||
import ustruct as struct
 | 
			
		||||
from ubinascii import hexlify
 | 
			
		||||
 | 
			
		||||
class MQTTException(Exception):
 | 
			
		||||
    pass
 | 
			
		||||
 | 
			
		||||
class MQTTClient:
 | 
			
		||||
 | 
			
		||||
    def __init__(self, client_id, server, port=0, user=None, password=None, keepalive=0,
 | 
			
		||||
                 ssl=False, ssl_params={}):
 | 
			
		||||
        if port == 0:
 | 
			
		||||
            port = 8883 if ssl else 1883
 | 
			
		||||
        self.client_id = client_id
 | 
			
		||||
        self.sock = None
 | 
			
		||||
        self.server = server
 | 
			
		||||
        self.port = port
 | 
			
		||||
        self.ssl = ssl
 | 
			
		||||
        self.ssl_params = ssl_params
 | 
			
		||||
        self.pid = 0
 | 
			
		||||
        self.cb = None
 | 
			
		||||
        self.user = user
 | 
			
		||||
        self.pswd = password
 | 
			
		||||
        self.keepalive = keepalive
 | 
			
		||||
        self.lw_topic = None
 | 
			
		||||
        self.lw_msg = None
 | 
			
		||||
        self.lw_qos = 0
 | 
			
		||||
        self.lw_retain = False
 | 
			
		||||
 | 
			
		||||
    def _send_str(self, s):
 | 
			
		||||
        self.sock.write(struct.pack("!H", len(s)))
 | 
			
		||||
        self.sock.write(s)
 | 
			
		||||
 | 
			
		||||
    def _recv_len(self):
 | 
			
		||||
        n = 0
 | 
			
		||||
        sh = 0
 | 
			
		||||
        while 1:
 | 
			
		||||
            b = self.sock.read(1)[0]
 | 
			
		||||
            n |= (b & 0x7f) << sh
 | 
			
		||||
            if not b & 0x80:
 | 
			
		||||
                return n
 | 
			
		||||
            sh += 7
 | 
			
		||||
 | 
			
		||||
    def set_callback(self, f):
 | 
			
		||||
        self.cb = f
 | 
			
		||||
 | 
			
		||||
    def set_last_will(self, topic, msg, retain=False, qos=0):
 | 
			
		||||
        assert 0 <= qos <= 2
 | 
			
		||||
        assert topic
 | 
			
		||||
        self.lw_topic = topic
 | 
			
		||||
        self.lw_msg = msg
 | 
			
		||||
        self.lw_qos = qos
 | 
			
		||||
        self.lw_retain = retain
 | 
			
		||||
 | 
			
		||||
    def connect(self, clean_session=True):
 | 
			
		||||
        self.sock = socket.socket()
 | 
			
		||||
        addr = socket.getaddrinfo(self.server, self.port)[0][-1]
 | 
			
		||||
        self.sock.connect(addr)
 | 
			
		||||
        if self.ssl:
 | 
			
		||||
            import ussl
 | 
			
		||||
            self.sock = ussl.wrap_socket(self.sock, **self.ssl_params)
 | 
			
		||||
        premsg = bytearray(b"\x10\0\0\0\0\0")
 | 
			
		||||
        msg = bytearray(b"\x04MQTT\x04\x02\0\0")
 | 
			
		||||
 | 
			
		||||
        sz = 10 + 2 + len(self.client_id)
 | 
			
		||||
        msg[6] = clean_session << 1
 | 
			
		||||
        if self.user is not None:
 | 
			
		||||
            sz += 2 + len(self.user) + 2 + len(self.pswd)
 | 
			
		||||
            msg[6] |= 0xC0
 | 
			
		||||
        if self.keepalive:
 | 
			
		||||
            assert self.keepalive < 65536
 | 
			
		||||
            msg[7] |= self.keepalive >> 8
 | 
			
		||||
            msg[8] |= self.keepalive & 0x00FF
 | 
			
		||||
        if self.lw_topic:
 | 
			
		||||
            sz += 2 + len(self.lw_topic) + 2 + len(self.lw_msg)
 | 
			
		||||
            msg[6] |= 0x4 | (self.lw_qos & 0x1) << 3 | (self.lw_qos & 0x2) << 3
 | 
			
		||||
            msg[6] |= self.lw_retain << 5
 | 
			
		||||
 | 
			
		||||
        i = 1
 | 
			
		||||
        while sz > 0x7f:
 | 
			
		||||
            premsg[i] = (sz & 0x7f) | 0x80
 | 
			
		||||
            sz >>= 7
 | 
			
		||||
            i += 1
 | 
			
		||||
        premsg[i] = sz
 | 
			
		||||
 | 
			
		||||
        self.sock.write(premsg, i + 2)
 | 
			
		||||
        self.sock.write(msg)
 | 
			
		||||
        #print(hex(len(msg)), hexlify(msg, ":"))
 | 
			
		||||
        self._send_str(self.client_id)
 | 
			
		||||
        if self.lw_topic:
 | 
			
		||||
            self._send_str(self.lw_topic)
 | 
			
		||||
            self._send_str(self.lw_msg)
 | 
			
		||||
        if self.user is not None:
 | 
			
		||||
            self._send_str(self.user)
 | 
			
		||||
            self._send_str(self.pswd)
 | 
			
		||||
        resp = self.sock.read(4)
 | 
			
		||||
        assert resp[0] == 0x20 and resp[1] == 0x02
 | 
			
		||||
        if resp[3] != 0:
 | 
			
		||||
            raise MQTTException(resp[3])
 | 
			
		||||
        return resp[2] & 1
 | 
			
		||||
 | 
			
		||||
    def disconnect(self):
 | 
			
		||||
        self.sock.write(b"\xe0\0")
 | 
			
		||||
        self.sock.close()
 | 
			
		||||
 | 
			
		||||
    def ping(self):
 | 
			
		||||
        self.sock.write(b"\xc0\0")
 | 
			
		||||
 | 
			
		||||
    def publish(self, topic, msg, retain=False, qos=0):
 | 
			
		||||
        pkt = bytearray(b"\x30\0\0\0")
 | 
			
		||||
        pkt[0] |= qos << 1 | retain
 | 
			
		||||
        sz = 2 + len(topic) + len(msg)
 | 
			
		||||
        if qos > 0:
 | 
			
		||||
            sz += 2
 | 
			
		||||
        assert sz < 2097152
 | 
			
		||||
        i = 1
 | 
			
		||||
        while sz > 0x7f:
 | 
			
		||||
            pkt[i] = (sz & 0x7f) | 0x80
 | 
			
		||||
            sz >>= 7
 | 
			
		||||
            i += 1
 | 
			
		||||
        pkt[i] = sz
 | 
			
		||||
        #print(hex(len(pkt)), hexlify(pkt, ":"))
 | 
			
		||||
        self.sock.write(pkt, i + 1)
 | 
			
		||||
        self._send_str(topic)
 | 
			
		||||
        if qos > 0:
 | 
			
		||||
            self.pid += 1
 | 
			
		||||
            pid = self.pid
 | 
			
		||||
            struct.pack_into("!H", pkt, 0, pid)
 | 
			
		||||
            self.sock.write(pkt, 2)
 | 
			
		||||
        self.sock.write(msg)
 | 
			
		||||
        if qos == 1:
 | 
			
		||||
            while 1:
 | 
			
		||||
                op = self.wait_msg()
 | 
			
		||||
                if op == 0x40:
 | 
			
		||||
                    sz = self.sock.read(1)
 | 
			
		||||
                    assert sz == b"\x02"
 | 
			
		||||
                    rcv_pid = self.sock.read(2)
 | 
			
		||||
                    rcv_pid = rcv_pid[0] << 8 | rcv_pid[1]
 | 
			
		||||
                    if pid == rcv_pid:
 | 
			
		||||
                        return
 | 
			
		||||
        elif qos == 2:
 | 
			
		||||
            assert 0
 | 
			
		||||
 | 
			
		||||
    def subscribe(self, topic, qos=0):
 | 
			
		||||
        assert self.cb is not None, "Subscribe callback is not set"
 | 
			
		||||
        pkt = bytearray(b"\x82\0\0\0")
 | 
			
		||||
        self.pid += 1
 | 
			
		||||
        struct.pack_into("!BH", pkt, 1, 2 + 2 + len(topic) + 1, self.pid)
 | 
			
		||||
        #print(hex(len(pkt)), hexlify(pkt, ":"))
 | 
			
		||||
        self.sock.write(pkt)
 | 
			
		||||
        self._send_str(topic)
 | 
			
		||||
        self.sock.write(qos.to_bytes(1, "little"))
 | 
			
		||||
        while 1:
 | 
			
		||||
            op = self.wait_msg()
 | 
			
		||||
            if op == 0x90:
 | 
			
		||||
                resp = self.sock.read(4)
 | 
			
		||||
                #print(resp)
 | 
			
		||||
                assert resp[1] == pkt[2] and resp[2] == pkt[3]
 | 
			
		||||
                if resp[3] == 0x80:
 | 
			
		||||
                    raise MQTTException(resp[3])
 | 
			
		||||
                return
 | 
			
		||||
 | 
			
		||||
    # Wait for a single incoming MQTT message and process it.
 | 
			
		||||
    # Subscribed messages are delivered to a callback previously
 | 
			
		||||
    # set by .set_callback() method. Other (internal) MQTT
 | 
			
		||||
    # messages processed internally.
 | 
			
		||||
    def wait_msg(self):
 | 
			
		||||
        res = self.sock.read(1)
 | 
			
		||||
        self.sock.setblocking(True)
 | 
			
		||||
        if res is None:
 | 
			
		||||
            return None
 | 
			
		||||
        if res == b"":
 | 
			
		||||
            raise OSError(-1)
 | 
			
		||||
        if res == b"\xd0":  # PINGRESP
 | 
			
		||||
            sz = self.sock.read(1)[0]
 | 
			
		||||
            assert sz == 0
 | 
			
		||||
            return None
 | 
			
		||||
        op = res[0]
 | 
			
		||||
        if op & 0xf0 != 0x30:
 | 
			
		||||
            return op
 | 
			
		||||
        sz = self._recv_len()
 | 
			
		||||
        topic_len = self.sock.read(2)
 | 
			
		||||
        topic_len = (topic_len[0] << 8) | topic_len[1]
 | 
			
		||||
        topic = self.sock.read(topic_len)
 | 
			
		||||
        sz -= topic_len + 2
 | 
			
		||||
        if op & 6:
 | 
			
		||||
            pid = self.sock.read(2)
 | 
			
		||||
            pid = pid[0] << 8 | pid[1]
 | 
			
		||||
            sz -= 2
 | 
			
		||||
        msg = self.sock.read(sz)
 | 
			
		||||
        self.cb(topic, msg)
 | 
			
		||||
        if op & 6 == 2:
 | 
			
		||||
            pkt = bytearray(b"\x40\x02\0\0")
 | 
			
		||||
            struct.pack_into("!H", pkt, 2, pid)
 | 
			
		||||
            self.sock.write(pkt)
 | 
			
		||||
        elif op & 6 == 4:
 | 
			
		||||
            assert 0
 | 
			
		||||
 | 
			
		||||
    # Checks whether a pending message from server is available.
 | 
			
		||||
    # If not, returns immediately with None. Otherwise, does
 | 
			
		||||
    # the same processing as wait_msg.
 | 
			
		||||
    def check_msg(self):
 | 
			
		||||
        self.sock.setblocking(False)
 | 
			
		||||
        return self.wait_msg()
 | 
			
		||||
		Reference in New Issue
	
	Block a user