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
+