diff --git a/README.md b/README.md index a3eb63e..e01ff20 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,11 @@ All documentation is on: https://shop.switchdoc.com/products/skyweather-raspberry-pi-based-weather-station-kit-for-the-cloud -Version V039 +Version V040 - EXPERIMENTAL! http://www.switchdoc.com/ +July 8, 2019: Version 040 - WeatherUnderground Fix, Support for SHT30 - EXPERIMENTAL!
June 5, 2019: Version 039 - AM2315 Reliablity Fix
May 21, 2019: Version 038 - Blynk Bug Fix
May 21, 2019: Version 037 - Blynk Changes / Bug Fix
diff --git a/SDL_Pi_SHT30/.gitignore b/SDL_Pi_SHT30/.gitignore new file mode 100644 index 0000000..3223751 --- /dev/null +++ b/SDL_Pi_SHT30/.gitignore @@ -0,0 +1,12 @@ +*.pyc +tx* +.*.swp +*.xml +*.temp +*.test +nohup.out +.*DS_Store +conflocal.py +state/EnglishMetric.txt +static/SkyCamera.py +static/*.png diff --git a/SDL_Pi_SHT30/LICENSE b/SDL_Pi_SHT30/LICENSE new file mode 100644 index 0000000..c151d06 --- /dev/null +++ b/SDL_Pi_SHT30/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Robert Wolterman + +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. diff --git a/SDL_Pi_SHT30/README.md b/SDL_Pi_SHT30/README.md new file mode 100644 index 0000000..769bb3a --- /dev/null +++ b/SDL_Pi_SHT30/README.md @@ -0,0 +1,27 @@ +# +# SDL_Pi_SHT30 +# +# SHT30 Pure Python Library +# SwitchDoc Labs July 2019 +# +# + +Version 1.1: July 8, 2019: Initial Version + + +#Introduction + +For the SwitchDoc Labs SHT30
+ + + +# testing + +``` +import SHT30 +sens = SHT30.SHT30() +print sens.read_temperature() +print sens.read_humidity() +print sens.read_humidity_temperature() +print sens.read_humidity_temperature_crc() +``` diff --git a/SDL_Pi_SHT30/SHT30.py b/SDL_Pi_SHT30/SHT30.py new file mode 100644 index 0000000..91014c5 --- /dev/null +++ b/SDL_Pi_SHT30/SHT30.py @@ -0,0 +1,233 @@ +#!usr/bin/python +# SwitchDoc Labs, 2019 +# added more reliablity functions including GrovePower Save + + +# MODULE IMPORTS +import time + +import smbus + +import traceback +import RPi.GPIO as GPIO + +GPIO.setmode(GPIO.BCM) + +# GLOBAL VARIABLES +SHT30_I2CADDR = 0x44 +SHT30_READCOMMAND = 0x2C +SHT30_READREG = 0x00 +MAXREADATTEMPT = 10 +SHT30_POLYNOMIAL = 0x131 # P(x) = x^8 + x^5 + x^4 + 1 = 100110001 + +SHT30DEBUG = False + +class SHT30: + """Base functionality for SHT30 humidity and temperature sensor. """ + + def __init__(self, address=SHT30_I2CADDR, i2c=None, powerpin=0, **kwargs): + + i2c = smbus.SMBus(1) + self.powerpin = powerpin + # for Grove PowerSave + if (self.powerpin <> 0): + GPIO.setup(self.powerpin, GPIO.OUT) + GPIO.output(self.powerpin, True) + time.sleep(1.0) + + self._device = i2c + self.humidity = 0 + self.temperature = 0 + self.crcT = 0 + self.crcH = 0 + self.SHT30PreviousTemp = -1000 + self.goodreads = 0 + self.badreadings = 0 + self.badcrcs = 0 + self.retrys = 0 + self.powercycles = 0 + + def powerCycleSHT30(self): + if (SHT30DEBUG == True): + print ("power cycling SHT30") + GPIO.output(self.powerpin, False) + time.sleep(10.50) + GPIO.output(self.powerpin, True) + time.sleep(1.50) + self.powercycles += 1 + + def verify_crc(self, data): + crc = 0xff + for byte in data: + crc ^= byte + for _ in range(8): + if crc & 0x80: + crc <<= 1 + crc ^= SHT30_POLYNOMIAL + else: + crc <<= 1 + return crc + + + + # fast read for device detection without faults + def _fast_read_data(self): + + + # TELL THE DEVICE WE WANT 4 BYTES OF DATA + self._device.write_i2c_block_data(SHT30_I2CADDR,SHT30_READCOMMAND,[0x06]) + time.sleep(0.5) + tmp = self._device.read_i2c_block_data(SHT30_I2CADDR,SHT30_READREG,6) + print "tmp=", tmp + TRaw = (((tmp[0] & 0x7F) << 8) | tmp[1]) + HRaw = ((tmp[3] << 8) | tmp[4]) + self.temperature = ((TRaw * 175) / 65535.0) - 45 + self.humidity = 100 * (HRaw) / 65535.0 + + self.crcT = tmp[2] + self.crcH = tmp[5] + # Verify CRC here + # force CRC error with the next line + #tmp[0] = tmp[0]+1 + tT = bytearray([tmp[0], tmp[1]]) + crcTC = self.verify_crc(tT) + tH = bytearray([tmp[3], tmp[4]]) + crcHC = self.verify_crc(tH) + + if (SHT30DEBUG == True): + print "SHT30temperature=",self.temperature + print "SHT30humdity=",self.humidity + print "SHT30crcTR=",self.crcT + print "SHT30crcTC=",crcTC + print "SHT30crcHR=",self.crcH + print "SHT30crcHC=",crcHC + + if (self.crcT != crcTC) or (self.crcH != crcHC): + if (SHT30DEBUG == True): + print "AM2314 BAD CRC" + self.crc = -1 + + + def _read_data(self): + count = 0 + tmp = None + powercyclecount = 0 + while count <= MAXREADATTEMPT: + try: + self._device.write_i2c_block_data(SHT30_I2CADDR,SHT30_READCOMMAND,[0x06]) + time.sleep(0.5) + tmp = self._device.read_i2c_block_data(SHT30_I2CADDR,SHT30_READREG,6) + + TRaw = (((tmp[0] & 0x7F) << 8) | tmp[1]) + HRaw = ((tmp[3] << 8) | tmp[4]) + self.temperature = ((TRaw * 175) / 65535.0) - 45 + self.humidity = 100 * (HRaw) / 65535.0 + + self.crcT = tmp[2] + self.crcH = tmp[5] + # check for > 10.0 degrees higher + if (self.SHT30PreviousTemp != -1000): # ignore first time + if (self.humidity <0.01 or self.humidity > 100.0): + # OK, humidity is bad. Ignore + if (SHT30DEBUG == True): + print ">>>>>>>>>>>>>" + print "Bad SHT30 Humidity = ", self.temperature + print ">>>>>>>>>>>>>" + self.badreadings = self.badreadings+1 + tmp = None + else: + if (abs(self.temperature - self.SHT30PreviousTemp) > 10.0): + # OK, temp is bad. Ignore + if (SHT30DEBUG == True): + print ">>>>>>>>>>>>>" + print "Bad SHT30 Humidity = ", self.temperature + print ">>>>>>>>>>>>>" + self.badreadings = self.badreadings+1 + tmp = None + else: + # Good Temperature + self.SHT30PreviousTemp = self.temperature + else: + # assume first is good temperature + self.SHT30PreviousTemp = self.temperature + # IF WE HAVE DATA, LETS EXIT THIS LOOP + if tmp != None: + break + except Exception as ex: + if (SHT30DEBUG == True): + template = "An exception of type {0} occurred. Arguments:\n{1!r}" + message = template.format(type(ex).__name__, ex.args) + print message + print traceback.format_exc() + print "SHT30readCount = ", count + count += 1 + self.retrys += 1 + time.sleep(0.10) + # only do three power cycle attempts + if (self.powerpin <> 0): + if (count > MAXREADATTEMPT): + self.powerCycleSHT30() + if (powercyclecount <=2): + powercyclecount +1 + count = 0 + + # GET THE DATA OUT OF THE LIST WE READ + TRaw = (((tmp[0] & 0x7F) << 8) | tmp[1]) + HRaw = ((tmp[3] << 8) | tmp[4]) + self.temperature = ((TRaw * 175) / 65535.0) - 45 + self.humidity = 100 * (HRaw) / 65535.0 + + self.crcT = tmp[2] + self.crcH = tmp[5] + # Verify CRC here + # force CRC error with the next line + # tmp[0] = tmp[0]+1 + tT = bytearray([tmp[0], tmp[1]]) + crcTC = self.verify_crc(tT) + tH = bytearray([tmp[3], tmp[4]]) + crcHC = self.verify_crc(tH) + + if (SHT30DEBUG == True): + print "SHT30temperature=",self.temperature + print "SHT30humdity=",self.humidity + print "SHT30crcTR=",self.crcT + print "SHT30crcTC=",crcTC + print "SHT30crcHR=",self.crcH + print "SHT30crcHC=",crcHC + + if (self.crcT != crcTC) or (self.crcH != crcHC): + if (SHT30DEBUG == True): + print "SHT30 BAD CRC" + self.badcrcs = self.badcrcs + 1 + self.crc = -1 + else: + self.goodreads = self.goodreads+1 + + def fast_read_temperature(self): + self._fast_read_data() + return self.temperature + + def read_temperature(self): + self._read_data() + return self.temperature + + def read_humidity(self): + self._read_data() + return self.humidity + + def read_humidity_temperature(self): + self._read_data() + return (self.humidity, self.temperature) + + def read_humidity_temperature_crc(self): + self._read_data() + return (self.humidity, self.temperature, self.crcH, self.crcT) + + def fast_read_humidity_temperature_crc(self): + self._fast_read_data() + return (self.humidity, self.temperature, self.crc, self.crcT) + + def read_status_info(self): + return (self.goodreads, self.badreadings, self.badcrcs, self.retrys,self.powercycles) + + diff --git a/SDL_Pi_SHT30/testSHT30.py b/SDL_Pi_SHT30/testSHT30.py new file mode 100644 index 0000000..0bc4e8f --- /dev/null +++ b/SDL_Pi_SHT30/testSHT30.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python + +import SHT30 +thsen = SHT30.SHT30(powerpin=6) + +while (1): + print "T ", thsen.read_temperature() + print "H ", thsen.read_humidity() + print "H,T ", thsen.read_humidity_temperature() + print "H,T,C ", thsen.read_humidity_temperature_crc() + h,t,cH,cT = thsen.read_humidity_temperature_crc() + print "CRCH=0x%02x" % cH + print "CRCT=0x%02x" % cT + diff --git a/SkyWeather.py b/SkyWeather.py index a902d0e..c10e0e4 100644 --- a/SkyWeather.py +++ b/SkyWeather.py @@ -14,7 +14,7 @@ except ImportError: import config -config.SWVERSION = "039" +config.SWVERSION = "040" import sys @@ -55,6 +55,7 @@ sys.path.append('./graphs') sys.path.append('./SDL_Pi_HDC1000') sys.path.append('./SDL_Pi_AM2315') +sys.path.append('./SDL_Pi_SHT30') sys.path.append('./BME680') sys.path.append('./SDL_Pi_GrovePowerDrive') @@ -689,6 +690,42 @@ def handle_as3935_interrupt(channel): GPIO.add_event_detect(as3935pin, GPIO.RISING, callback=handle_as3935_interrupt) +############## +# Setup SHT30 +# turn I2CBus 0 on +if (config.TCA9545_I2CMux_Present): + tca9545.write_control_register(TCA9545_CONFIG_BUS0) + +# Grove Power Save Pins for device reset + +############### + +# Detect SHT30 +outsideHumidity = 0.0 +outsideTemperature = 0.0 +crc_check = -1 +import SHT30 +try: + sht30 = SHT30.SHT30(powerpin=config.SHT30GSPIN ) + outsideHumidity, outsideTemperature, crc_checkH, crc_checkT = sht30.read_humidity_temperature_crc() + + print "outsideTemperature: %0.1f C" % outsideTemperature + print "outsideHumidity: %0.1f %%" % outsideHumidity + state.currentOutsideTemperature = outsideTemperature + state.currentOutsideHumidity = outsideHumidity + print "crcH: 0x%02x" % crc_checkH + print "crcT 0x%02x" % crc_checkT + config.SHT30_Present = True + if (crc_checkH == -1) or (crc_checkT == -1): + config.SHT30_Present = False + +except: + config.SHT30_Present = False + + + +print "after SHT30" + ############## # Setup AM2315 # turn I2CBus 0 on @@ -697,7 +734,6 @@ def handle_as3935_interrupt(channel): # Grove Power Save Pins for device reset -config.AM2315GSPIN = 6 ############### @@ -708,8 +744,8 @@ def handle_as3935_interrupt(channel): import AM2315 try: am2315 = AM2315.AM2315(powerpin=config.AM2315GSPIN ) - outsideHumidity, outsideTemperature, crc_check = am2315.read_humidity_temperature_crc() - #outsideHumidity, outsideTemperature, crc_check = am2315.fast_read_humidity_temperature_crc() + #outsideHumidity, outsideTemperature, crc_check = am2315.read_humidity_temperature_crc() + outsideHumidity, outsideTemperature, crc_check = am2315.fast_read_humidity_temperature_crc() print "outsideTemperature: %0.1f C" % outsideTemperature print "outsideHumidity: %0.1f %%" % outsideHumidity state.currentOutsideTemperature = outsideTemperature @@ -1008,8 +1044,9 @@ def sampleWeather(): as3935InterruptStatus = "No AS3935 Lightning Detector Present" as3935LastInterrupt = 0x00 - if (config.WXLink_Present == False): # do not use internal AM2315 if we are WXLink connected - if (config.AM2315_Present): + if (config.WXLink_Present == False): # do not use internal AM2315 or SHT30 if we are WXLink connected + # if both AM2315 and SHT30 are present, SHT30 wins + if (config.AM2315_Present) and ( config.SHT30_Present == False): # get AM2315 Outside Humidity and Outside Temperature # turn I2CBus 0 on if (config.TCA9545_I2CMux_Present): @@ -1031,6 +1068,24 @@ def sampleWeather(): if (config.SWDEBUG == True): print "AM2315 Stats: (g,br,bc,rt,pc)", am2315.read_status_info() + # if both AM2315 and SHT30 are present, SHT30 wins + if (config.SHT30_Present): + # get SHT30 Outside Humidity and Outside Temperature + # turn I2CBus 0 on + if (config.TCA9545_I2CMux_Present): + tca9545.write_control_register(TCA9545_CONFIG_BUS0) + + ToutsideHumidity, ToutsideTemperature, crc_checkH, crc_checkT = sht30.read_humidity_temperature_crc() + + + if (crc_checkH != -1) and (crc_checkT != -1): + outsideTemperature = ToutsideTemperature + outsideHumidity = ToutsideHumidity + state.currentOutsideTemperature = outsideTemperature + state.currentOutsideHumidity = outsideHumidity + if (config.SWDEBUG == True): + print "SHT30 Stats: (g,br,bc,rt,pc)", sht30.read_status_info() + if (config.WeatherUnderground_Present == True): @@ -1584,12 +1639,13 @@ def checkForButtons(): print returnStatusLine("SkyCam",config.Camera_Present) print returnStatusLine("DS3231",config.DS3231_Present) print returnStatusLine("HDC1080",config.HDC1080_Present) +print returnStatusLine("SHT30",config.SHT30_Present) print returnStatusLine("AM2315",config.AM2315_Present) print returnStatusLine("ADS1015",config.ADS1015_Present) print returnStatusLine("ADS1115",config.ADS1115_Present) print returnStatusLine("AS3935",config.AS3935_Present) print returnStatusLine("OLED",config.OLED_Present) -print returnStatusLine("SunAirPlus",config.SunAirPlus_Present) +print returnStatusLine("SunAirPlus/SunControl",config.SunAirPlus_Present) print returnStatusLine("SI1145 Sun Sensor",config.Sunlight_Present) print returnStatusLine("TSL2591 Sun Sensor",config.TSL2591_Present) print returnStatusLine("DustSensor",config.DustSensor_Present) diff --git a/WeatherUnderground.py b/WeatherUnderground.py index 6a5f1cf..5157c17 100644 --- a/WeatherUnderground.py +++ b/WeatherUnderground.py @@ -41,7 +41,7 @@ def sendWeatherUndergroundData( as3935LightningCount, as3935, as3935LastInterrup myURL += "&rainin=%0.2f" % ((rain60Minutes)/25.4) myURL += "&dailyrainin=%0.2f" % ((totalRain)/25.4) - myURL += "&baromin=%0.2f" % ((bmp180SeaLevel) * 0.2953) + myURL += "&baromin=%0.2f" % ((bmp180SeaLevel) * 0.2953)/10.0 myURL += "&indoortempf=%0.2f" % ((HTUtemperature*9.0/5.0)+32.0) myURL += "&indoorhumidity%0.2f=" % HTUhumidity diff --git a/config.py b/config.py index 31a524c..fda6c27 100644 --- a/config.py +++ b/config.py @@ -79,6 +79,7 @@ BMP280_Present = False BME680_Present = False HDC1080_Present = False +SHT30_Present = False AM2315_Present = False ADS1015_Present = False ADS1115_Present = False @@ -108,6 +109,7 @@ rainPin = 13 +SHT30GSPIN = 6 AM2315GSPIN = 6 # for fan diff --git a/static/skycamera.jpg b/static/skycamera.jpg new file mode 100644 index 0000000..1cc79eb Binary files /dev/null and b/static/skycamera.jpg differ diff --git a/testSHT30.py b/testSHT30.py new file mode 100644 index 0000000..decf215 --- /dev/null +++ b/testSHT30.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +import sys +sys.path.append('./SDL_Pi_TCA9545') + +import SDL_Pi_TCA9545 + + + + +################ +# TCA9545 I2C Mux + +#/*========================================================================= +# I2C ADDRESS/BITS +# -----------------------------------------------------------------------*/ +TCA9545_ADDRESS = (0x73) # 1110011 (A0+A1=VDD) +#/*=========================================================================*/ + +#/*========================================================================= +# CONFIG REGISTER (R/W) +# -----------------------------------------------------------------------*/ +TCA9545_REG_CONFIG = (0x00) +# /*---------------------------------------------------------------------*/ + +TCA9545_CONFIG_BUS0 = (0x01) # 1 = enable, 0 = disable +TCA9545_CONFIG_BUS1 = (0x02) # 1 = enable, 0 = disable +TCA9545_CONFIG_BUS2 = (0x04) # 1 = enable, 0 = disable +TCA9545_CONFIG_BUS3 = (0x08) # 1 = enable, 0 = disable + +#/*=========================================================================*/ + +# I2C Mux TCA9545 Detection +try: + tca9545 = SDL_Pi_TCA9545.SDL_Pi_TCA9545(addr=TCA9545_ADDRESS, bus_enable = TCA9545_CONFIG_BUS0) + + + # turn I2CBus 0 on + tca9545.write_control_register(TCA9545_CONFIG_BUS0) + TCA9545_I2CMux_Present = True +except: + print ">>>>>>>>>>>>>>>>>>><<<<<<<<<<<" + print "TCA9545 I2C Mux Not Present" + print ">>>>>>>>>>>>>>>>>>><<<<<<<<<<<" + + +sys.path.append('./SDL_Pi_SHT30') + +import SHT30 +thsen = SHT30.SHT30(powerpin=6) + +while (1): + print "T ", thsen.read_temperature() + print "H ", thsen.read_humidity() + print "H,T ", thsen.read_humidity_temperature() + print "H,T,C ", thsen.read_humidity_temperature_crc() + h,t,cH,cT = thsen.read_humidity_temperature_crc() + print "CRCH=0x%02x" % cH + print "CRCT=0x%02x" % cT +