From 042a5cb75be88701c6c080579c49dd783273bfa4 Mon Sep 17 00:00:00 2001
From: Stefan Huber <stefan.huber@stusta.de>
Date: Fri, 29 Nov 2019 13:22:32 +0100
Subject: [PATCH] for branch maxbill

---
 BME280.py        | 292 +++++++++++++++++++++++++++++++++++++++++++++++
 main.py          |  58 ++++++++++
 tempermon.ini    |  42 +++++--
 tempermonitor.py |   2 +-
 4 files changed, 381 insertions(+), 13 deletions(-)
 create mode 100644 BME280.py
 create mode 100644 main.py

diff --git a/BME280.py b/BME280.py
new file mode 100644
index 0000000..bfaa060
--- /dev/null
+++ b/BME280.py
@@ -0,0 +1,292 @@
+"""
+This code is designed to run on a ESP32 or ESP8266. It will
+control one single BME280 via I2C.
+"""
+from machine import I2C
+import time
+
+# BME280 default address.
+BME280_I2CADDR = 0x76
+
+# Operating Modes
+BME280_OSAMPLE_1 = 1
+BME280_OSAMPLE_2 = 2
+BME280_OSAMPLE_4 = 3
+BME280_OSAMPLE_8 = 4
+BME280_OSAMPLE_16 = 5
+
+# BME280 Registers
+
+BME280_REGISTER_DIG_T1 = 0x88  # Trimming parameter registers
+BME280_REGISTER_DIG_T2 = 0x8A
+BME280_REGISTER_DIG_T3 = 0x8C
+
+BME280_REGISTER_DIG_P1 = 0x8E
+BME280_REGISTER_DIG_P2 = 0x90
+BME280_REGISTER_DIG_P3 = 0x92
+BME280_REGISTER_DIG_P4 = 0x94
+BME280_REGISTER_DIG_P5 = 0x96
+BME280_REGISTER_DIG_P6 = 0x98
+BME280_REGISTER_DIG_P7 = 0x9A
+BME280_REGISTER_DIG_P8 = 0x9C
+BME280_REGISTER_DIG_P9 = 0x9E
+
+BME280_REGISTER_DIG_H1 = 0xA1
+BME280_REGISTER_DIG_H2 = 0xE1
+BME280_REGISTER_DIG_H3 = 0xE3
+BME280_REGISTER_DIG_H4 = 0xE4
+BME280_REGISTER_DIG_H5 = 0xE5
+BME280_REGISTER_DIG_H6 = 0xE6
+BME280_REGISTER_DIG_H7 = 0xE7
+
+BME280_REGISTER_CHIPID = 0xD0
+BME280_REGISTER_VERSION = 0xD1
+BME280_REGISTER_SOFTRESET = 0xE0
+
+BME280_REGISTER_CONTROL_HUM = 0xF2
+BME280_REGISTER_CONTROL = 0xF4
+BME280_REGISTER_CONFIG = 0xF5
+BME280_REGISTER_PRESSURE_DATA = 0xF7
+BME280_REGISTER_TEMP_DATA = 0xFA
+BME280_REGISTER_HUMIDITY_DATA = 0xFD
+
+
+class Device:
+  """Class for communicating with an I2C device.
+
+  Allows reading and writing 8-bit, 16-bit, and byte array values to
+  registers on the device."""
+
+  def __init__(self, address, i2c):
+    """Create an instance of the I2C device at the specified address using
+    the specified I2C interface object."""
+    self._address = address
+    self._i2c = i2c
+
+  def writeRaw8(self, value):
+    """Write an 8-bit value on the bus (without register)."""
+    value = value & 0xFF
+    self._i2c.writeto(self._address, value)
+
+  def write8(self, register, value):
+    """Write an 8-bit value to the specified register."""
+    b=bytearray(1)
+    b[0]=value & 0xFF
+    self._i2c.writeto_mem(self._address, register, b)
+
+  def write16(self, register, value):
+    """Write a 16-bit value to the specified register."""
+    value = value & 0xFFFF
+    b=bytearray(2)
+    b[0]= value & 0xFF
+    b[1]= (value>>8) & 0xFF
+    self.i2c.writeto_mem(self._address, register, value)
+
+  def readRaw8(self):
+    """Read an 8-bit value on the bus (without register)."""
+    return int.from_bytes(self._i2c.readfrom(self._address, 1),'little') & 0xFF
+
+  def readU8(self, register):
+    """Read an unsigned byte from the specified register."""
+    return int.from_bytes(
+        self._i2c.readfrom_mem(self._address, register, 1),'little') & 0xFF
+
+  def readS8(self, register):
+    """Read a signed byte from the specified register."""
+    result = self.readU8(register)
+    if result > 127:
+      result -= 256
+    return result
+
+  def readU16(self, register, little_endian=True):
+    """Read an unsigned 16-bit value from the specified register, with the
+    specified endianness (default little endian, or least significant byte
+    first)."""
+    result = int.from_bytes(
+        self._i2c.readfrom_mem(self._address, register, 2),'little') & 0xFFFF
+    if not little_endian:
+      result = ((result << 8) & 0xFF00) + (result >> 8)
+    return result
+
+  def readS16(self, register, little_endian=True):
+    """Read a signed 16-bit value from the specified register, with the
+    specified endianness (default little endian, or least significant byte
+    first)."""
+    result = self.readU16(register, little_endian)
+    if result > 32767:
+      result -= 65536
+    return result
+
+  def readU16LE(self, register):
+    """Read an unsigned 16-bit value from the specified register, in little
+    endian byte order."""
+    return self.readU16(register, little_endian=True)
+
+  def readU16BE(self, register):
+    """Read an unsigned 16-bit value from the specified register, in big
+    endian byte order."""
+    return self.readU16(register, little_endian=False)
+
+  def readS16LE(self, register):
+    """Read a signed 16-bit value from the specified register, in little
+    endian byte order."""
+    return self.readS16(register, little_endian=True)
+
+  def readS16BE(self, register):
+    """Read a signed 16-bit value from the specified register, in big
+    endian byte order."""
+    return self.readS16(register, little_endian=False)
+
+
+class BME280:
+  def __init__(self, mode=BME280_OSAMPLE_1, address=BME280_I2CADDR, i2c=None,
+               **kwargs):
+    # Check that mode is valid.
+    if mode not in [BME280_OSAMPLE_1, BME280_OSAMPLE_2, BME280_OSAMPLE_4,
+                    BME280_OSAMPLE_8, BME280_OSAMPLE_16]:
+        raise ValueError(
+            'Unexpected mode value {0}. Set mode to one of '
+            'BME280_ULTRALOWPOWER, BME280_STANDARD, BME280_HIGHRES, or '
+            'BME280_ULTRAHIGHRES'.format(mode))
+    self._mode = mode
+    # Create I2C device.
+    if i2c is None:
+      raise ValueError('An I2C object is required.')
+    self._device = Device(address, i2c)
+    # Load calibration values.
+    self._load_calibration()
+    self._device.write8(BME280_REGISTER_CONTROL, 0x3F)
+    self.t_fine = 0
+
+  def _load_calibration(self):
+
+    self.dig_T1 = self._device.readU16LE(BME280_REGISTER_DIG_T1)
+    self.dig_T2 = self._device.readS16LE(BME280_REGISTER_DIG_T2)
+    self.dig_T3 = self._device.readS16LE(BME280_REGISTER_DIG_T3)
+
+    self.dig_P1 = self._device.readU16LE(BME280_REGISTER_DIG_P1)
+    self.dig_P2 = self._device.readS16LE(BME280_REGISTER_DIG_P2)
+    self.dig_P3 = self._device.readS16LE(BME280_REGISTER_DIG_P3)
+    self.dig_P4 = self._device.readS16LE(BME280_REGISTER_DIG_P4)
+    self.dig_P5 = self._device.readS16LE(BME280_REGISTER_DIG_P5)
+    self.dig_P6 = self._device.readS16LE(BME280_REGISTER_DIG_P6)
+    self.dig_P7 = self._device.readS16LE(BME280_REGISTER_DIG_P7)
+    self.dig_P8 = self._device.readS16LE(BME280_REGISTER_DIG_P8)
+    self.dig_P9 = self._device.readS16LE(BME280_REGISTER_DIG_P9)
+
+    self.dig_H1 = self._device.readU8(BME280_REGISTER_DIG_H1)
+    self.dig_H2 = self._device.readS16LE(BME280_REGISTER_DIG_H2)
+    self.dig_H3 = self._device.readU8(BME280_REGISTER_DIG_H3)
+    self.dig_H6 = self._device.readS8(BME280_REGISTER_DIG_H7)
+
+    h4 = self._device.readS8(BME280_REGISTER_DIG_H4)
+    h4 = (h4 << 24) >> 20
+    self.dig_H4 = h4 | (self._device.readU8(BME280_REGISTER_DIG_H5) & 0x0F)
+
+    h5 = self._device.readS8(BME280_REGISTER_DIG_H6)
+    h5 = (h5 << 24) >> 20
+    self.dig_H5 = h5 | (
+        self._device.readU8(BME280_REGISTER_DIG_H5) >> 4 & 0x0F)
+
+  def read_raw_temp(self):
+    """Reads the raw (uncompensated) temperature from the sensor."""
+    meas = self._mode
+    self._device.write8(BME280_REGISTER_CONTROL_HUM, meas)
+    meas = self._mode << 5 | self._mode << 2 | 1
+    self._device.write8(BME280_REGISTER_CONTROL, meas)
+    sleep_time = 1250 + 2300 * (1 << self._mode)
+
+    sleep_time = sleep_time + 2300 * (1 << self._mode) + 575
+    sleep_time = sleep_time + 2300 * (1 << self._mode) + 575
+    time.sleep_us(sleep_time)  # Wait the required time
+    msb = self._device.readU8(BME280_REGISTER_TEMP_DATA)
+    lsb = self._device.readU8(BME280_REGISTER_TEMP_DATA + 1)
+    xlsb = self._device.readU8(BME280_REGISTER_TEMP_DATA + 2)
+    raw = ((msb << 16) | (lsb << 8) | xlsb) >> 4
+    return raw
+
+  def read_raw_pressure(self):
+    """Reads the raw (uncompensated) pressure level from the sensor."""
+    """Assumes that the temperature has already been read """
+    """i.e. that enough delay has been provided"""
+    msb = self._device.readU8(BME280_REGISTER_PRESSURE_DATA)
+    lsb = self._device.readU8(BME280_REGISTER_PRESSURE_DATA + 1)
+    xlsb = self._device.readU8(BME280_REGISTER_PRESSURE_DATA + 2)
+    raw = ((msb << 16) | (lsb << 8) | xlsb) >> 4
+    return raw
+
+  def read_raw_humidity(self):
+    """Assumes that the temperature has already been read """
+    """i.e. that enough delay has been provided"""
+    msb = self._device.readU8(BME280_REGISTER_HUMIDITY_DATA)
+    lsb = self._device.readU8(BME280_REGISTER_HUMIDITY_DATA + 1)
+    raw = (msb << 8) | lsb
+    return raw
+
+  def read_temperature(self):
+    """Get the compensated temperature in 0.01 of a degree celsius."""
+    adc = self.read_raw_temp()
+    var1 = ((adc >> 3) - (self.dig_T1 << 1)) * (self.dig_T2 >> 11)
+    var2 = ((
+        (((adc >> 4) - self.dig_T1) * ((adc >> 4) - self.dig_T1)) >> 12) *
+        self.dig_T3) >> 14
+    self.t_fine = var1 + var2
+    return (self.t_fine * 5 + 128) >> 8
+
+  def read_pressure(self):
+    """Gets the compensated pressure in Pascals."""
+    adc = self.read_raw_pressure()
+    var1 = self.t_fine - 128000
+    var2 = var1 * var1 * self.dig_P6
+    var2 = var2 + ((var1 * self.dig_P5) << 17)
+    var2 = var2 + (self.dig_P4 << 35)
+    var1 = (((var1 * var1 * self.dig_P3) >> 8) +
+            ((var1 * self.dig_P2) >> 12))
+    var1 = (((1 << 47) + var1) * self.dig_P1) >> 33
+    if var1 == 0:
+      return 0
+    p = 1048576 - adc
+    p = (((p << 31) - var2) * 3125) // var1
+    var1 = (self.dig_P9 * (p >> 13) * (p >> 13)) >> 25
+    var2 = (self.dig_P8 * p) >> 19
+    return ((p + var1 + var2) >> 8) + (self.dig_P7 << 4)
+
+  def read_humidity(self):
+    adc = self.read_raw_humidity()
+    # print 'Raw humidity = {0:d}'.format (adc)
+    h = self.t_fine - 76800
+    h = (((((adc << 14) - (self.dig_H4 << 20) - (self.dig_H5 * h)) +
+         16384) >> 15) * (((((((h * self.dig_H6) >> 10) * (((h *
+                          self.dig_H3) >> 11) + 32768)) >> 10) + 2097152) *
+                          self.dig_H2 + 8192) >> 14))
+    h = h - (((((h >> 15) * (h >> 15)) >> 7) * self.dig_H1) >> 4)
+    h = 0 if h < 0 else h
+    h = 419430400 if h > 419430400 else h
+    return h >> 12
+
+  @property
+  def temperature(self):
+    "Return the temperature in degrees."
+    t = self.read_temperature()
+    ti = t // 100
+    td = t - ti * 100
+    #return "{}.{:02d}C".format(ti, td)
+    return "{}.{:02d}".format(ti, td)
+
+  @property
+  def pressure(self):
+    "Return the temperature in hPa."
+    p = self.read_pressure() // 256
+    pi = p // 100
+    pd = p - pi * 100
+    #return "{}.{:02d}hPa".format(pi, pd)
+    return "{}.{:02d}".format(pi, pd)
+
+  @property
+  def humidity(self):
+    "Return the humidity in percent."
+    h = self.read_humidity()
+    hi = h // 1024
+    hd = h * 100 // 1024 - hi * 100
+    #return "{}.{:02d}%".format(hi, hd)
+    return "{}.{:02d}".format(hi, hd)
diff --git a/main.py b/main.py
new file mode 100644
index 0000000..ae8ed04
--- /dev/null
+++ b/main.py
@@ -0,0 +1,58 @@
+"""
+This code is designed to run on an ESP8266 for grabbing temperature data from
+ds18x20 sensors via the onewire protocol and sending them via serial to the
+connected host. It should be trivial to port it to different platforms.
+It also grabs data from one BME280 via I2C.
+
+The Protocol is dead simple:
+
+ONEWIRE-ID1 TEMPERATURE1 (in degree C)
+ONEWIRE-ID2 TEMPERATURE2 (in degree C)
+BME280-TEMPERATURE TEMPERATUREBME (in degree C)
+BME280-PRESSURE PRESSUREBME (in hPa)
+BME280-HUMIDITY HUMIDITYBME (in %rH)
+...
+<Empty Line>
+
+When a sensor has problems reading, it sends as temperature 9001.
+
+New sensors are only detected upon powerup, so you have to reboot in order to
+extend the sensor network.
+
+The sensors have a parasitic-power-mode which is NOT TO BE USED here.
+Please connect all three pins, and multiplex as you please.
+"""
+
+import machine
+import time
+import onewire, ds18x20
+import ubinascii
+import BME280
+
+di = machine.Pin(13)
+ds = ds18x20.DS18X20(onewire.OneWire(di))
+
+scl = machine.Pin(5)
+sda = machine.Pin(4)
+
+i2c = machine.I2C(scl=scl, sda=sda, freq=10000)
+bme = BME280.BME280(i2c=i2c)
+
+# scan for onewire sensors
+roms = ds.scan()
+
+while 1:
+    time.sleep_ms(240)
+    ds.convert_temp()
+    time.sleep_ms(750)
+    for rom in roms:
+        print(ubinascii.hexlify(rom).decode('utf-8'), end=' ')
+        try:
+            print(ds.read_temp(rom))
+        except:
+            print(9001)
+    print("BME280_temperature",bme.temperature)
+    print("BME280_pressure", bme.pressure)
+    print("BME280_humidity", bme.humidity)
+    print()
+
diff --git a/tempermon.ini b/tempermon.ini
index 6e98a68..244725f 100644
--- a/tempermon.ini
+++ b/tempermon.ini
@@ -1,6 +1,5 @@
 [serial]
-#port=/dev/ttyUSB0
-port=/tmp/temperature_pts
+port=/dev/ttyUSB0
 baudrate=115200
 timeout=100
 
@@ -12,23 +11,42 @@ interval=1
 
 [mail]
 from=Temperman <root@temperator.stusta.de>
-to=jw@stusta.de,markus.hefele@stusta.de
-to_urgent=jw@stusta.de,markus.hefele@stusta.de
+to=jw@stusta.de,markus.hefele@stusta.de,suhu@stusta.de
+to_urgent=vorstand@stustanet.de
 min_delay_between_messages=3600
 
 [warning]
-floor_sensors=Test
-ceiling_sensors=Test2
+floor_sensors=temp0
+ceiling_sensors=temp1
 
 min_ceiling_warning=35
 floor_ceiling_diff=15
 ceiling_warning_level=40
 
+[28aaabf6531401a0]
+name=temp0
+calibration=0
 
-[testsensor]
-name=Test
-calibration=5
+[28aab9ee5314017b]
+name=temp1
+calibration=0
 
-[sensortest]
-name=Test2
-calibration=5
+[28aa82ec53140188]
+name=temp2
+calibration=0
+
+[28aaec455414017b]
+name=temp3
+calibration=0
+
+[BME280_temperature]
+name=temp4
+calibration=0
+
+[BME280_pressure]
+name=pressure
+calibration=0
+
+[BME280_humidity]
+name=humidity
+calibration=0
diff --git a/tempermonitor.py b/tempermonitor.py
index 3c26e86..0e24c8c 100755
--- a/tempermonitor.py
+++ b/tempermonitor.py
@@ -174,7 +174,7 @@ class TempMonitor:
                                        config=self._configname,
                                        owid=owid,
                                        temp=temp)
-            elif temp > 1000 or temp < -1000:
+            elif temp > 9000 or temp < -1000:
                 sensor.valid = False
                 # if the sensor is giving bullshit data - notify the operators
                 await self.call_plugin("err_problem_sensor",
-- 
GitLab