I recently came across a really excellent solution for putting together 
your own weather station on the cheap with high quality sensors - and most 
importantly a very useful weather HAT to use with a Raspberry PI to connect 
all the sensors.

It comes complete with a 4 part step by step tutorial to assemble the whole 
station, and basic code to upload the data to and online service - see link 
below:

https://www.bc-robotics.com/tutorials/raspberry-pi-weather-station-part-1/

I completed my own installation based on the instructions in the tutorial 
and my station running on weewx is active at the link below

http://peachlandweather.ca/mystation/

For the outdoor portion I used a Raspberry Pi Zero W, with the weather 
HAT.  Weewx is running indoors in a FreeBSD jail, using a stock Interceptor 
driver.  I modified the basic upload code provided in the tutorial quite 
extensively to generate upload loops that matched the Observer URLs 
supported by the Interceptor driver, and also to produce derived 
observations not included in the original code.

The upload code is attached for anyone who might be interested in trying to 
assemble their own.

I   tested the results from using the station against my Vantage Pro 2 and 
so far it seems to be pretty close - for less than 20% of the cost of a 
Vantage Pro.

-- 
You received this message because you are subscribed to the Google Groups 
"weewx-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.
#!/usr/bin/python

import time
import datetime
from w1thermsensor import W1ThermSensor
from Adafruit_BME280 import *
import Adafruit_ADS1x15
import RPi.GPIO as GPIO
import requests
import syslog
import pickle
 
ds18b20 = W1ThermSensor()
bme = BME280(t_mode=BME280_OSAMPLE_8, p_mode=BME280_OSAMPLE_8, h_mode=BME280_OSAMPLE_8)
adc = Adafruit_ADS1x15.ADS1115()

## Settings to control operation of weather station
 
interval = 15  #How long we want to wait between loops (seconds)
loglevel = 0   #Set to 0 for no log entries, set to 1 for basic log info and 2 for all log info
upload_url = "http://192.168.0.29";  #IP address or URL to upload data, inlcude port number if not standard
filename = "/home/pi/dailyrainfile"
 
## Init settings for variable - do not adjust

windTick = 0   #Used to count the number of times the wind speed input is triggered
rainTick = 0   #Used to count the number of times the rain input is triggered
ticktimenow = None   # Used to calculate the wind gust be determining the shortest time between windTicks 
mindeltatime = None  # Used to calculate the wind gust be determining the shortest time between windTicks
looptimenow = time.time()  #Used to calculate the wind gust be determining the shortest time between windTicks

#load previous saved dailyrain value
try:
    infile = open(filename,'rb')
    saved_dailyrain = pickle.load(infile)
    infile.close()
    dailyrain = saved_dailyrain
    syslog.syslog("Loading of saved dailyrain state success. Initialise dailyrain to %s" % dailyrain) 
except:
    syslog.syslog("Loading of saved dailyrain state failed. Initialise dailyrain to zero")
    dailyrain = 0

#Set GPIO pins to use BCM pin numbers
GPIO.setmode(GPIO.BCM)
 
#Set digital pin 17 to an input and enable the pullup 
GPIO.setup(17, GPIO.IN, pull_up_down=GPIO.PUD_UP)
 
#Set digital pin 23 to an input and enable the pullup 
GPIO.setup(23, GPIO.IN, pull_up_down=GPIO.PUD_UP)
 
#Event to detect wind (4 ticks per revolution)
GPIO.add_event_detect(17, GPIO.BOTH) 

def windtrig(self):
    global windTick
    windTick += 1
    global ticktimenow
    if ticktimenow is None:
        ticktimenow = time.time()
    else:
        ticktimelast = ticktimenow
        ticktimenow = time.time()
        tickdelta = ticktimenow - ticktimelast
#        print 'Delta time:    ' , tickdelta, 'Seconds'    
        global mindeltatime
        if mindeltatime is None:
            mindeltatime = tickdelta
        else:
            if tickdelta < mindeltatime:
                mindeltatime = tickdelta
#            print 'Min Delta Time:    ' , mindeltatime, 'Seconds'
 
GPIO.add_event_callback(17, windtrig)
 
#Event to detect rainfall tick
GPIO.add_event_detect(23, GPIO.FALLING)
def raintrig(self):
    global rainTick
    rainTick += 1
 
GPIO.add_event_callback(23, raintrig)
 
#Function to determine the start of day timestamp
def startOfDay(time_ts):
    """Calculate the unix epoch time for the start of a (local time) day.
    
    time_ts: A timestamp somewhere in the day for which the start-of-day
    is desired.
    
    returns: The timestamp for the start-of-day (00:00) in unix epoch time.
    
    """
    _time_tt = time.localtime(time_ts)
    _bod_ts = time.mktime((_time_tt.tm_year,
                            _time_tt.tm_mon,
                            _time_tt.tm_mday,
                            0, 0, 0, 0, 0, -1))
    return int(_bod_ts)
 
while True:
 
    time.sleep(interval)
 
    #Pull Temperature from DS18B20
    outtemp = ds18b20.get_temperature()
    #tempf = (tempc * 9 / 5) + 32
 
    #Pull temperature from BME280
    case_temp = bme.read_temperature()
 
    #Pull pressure from BME280 Sensor & convert to kPa
    pressure_pa = bme.read_pressure()
    absbaro = pressure_pa / 100
 
    #Pull humidity from BME280
    outhumi = bme.read_humidity()
 
    #Calculate wind direction based on ADC reading

    try:
        val = adc.read_adc(0, gain=1) #Read ADC channel 0 with a gain setting of 1
        if 20000 <= val <= 20500:
            winddir = 0
        if 10000 <= val <= 10500:
            winddir = 22.5
        if 11500 <= val <= 12000:
            winddir = 45
        if 2000 <= val <= 2250:
            winddir = 67.5 
        if 2300 <= val <= 2500:
            winddir = 90
        if 1500 <= val <= 1950:
            winddir = 112.5
        if 4500 <= val <= 4900:
            winddir = 135
        if 3000 <= val <= 3500:
            winddir = 157.5
        if 7000 <= val <= 7500:
            winddir = 180
        if 6000 <= val <= 6500:
            winddir = 202.5
        if 16000 <= val <= 16500:
            winddir = 225
        if 15000 <= val <= 15500:
            winddir = 247.5
        if 24000 <= val <= 24500:
            winddir = 270
        if 21000 <= val <= 21500:
            winddir = 292.5
        if 22500 <= val <= 23000:
            winddir = 315
        if 17500 <= val <= 18500:
            winddir = 337.5
    except:
        winddir = None
        syslog.syslog('Wind Direction Input not valid')
 
    #Calculate average windspeed over the last 15 seconds
    windspeed = (windTick * 1.2) / interval
#    print 'Wind Ticks:  ' , windTick
    windTick = 0
    if mindeltatime is not None:
        maxticks = 1 / mindeltatime
        windgust = maxticks * 1.2
        mindeltatime = None
    else:
        windgust = 0 
        maxticks = 0
 
    #Calculate accumulated rainfall over the last 15 seconds
    rain = rainTick * 0.2794

    #Calculate accumulated rainfull over the last calendar day
    looptimelast = looptimenow
    looptimenow = time.time()

    if startOfDay(looptimelast) == startOfDay(looptimenow):
        dailyrain = dailyrain + rain
    else:
        dailyrain = rain

    if 'saved_dailyrain' in globals():
        if dailyrain <> saved_dailyrain:
            saved_dailyrain = dailyrain
            outfile = open(filename,'wb')
            pickle.dump(saved_dailyrain,outfile)
            outfile.close()
            if loglevel == 2:
                syslog.syslog('Saved updated dailyrain %s' % dailyrain)
    else:
        saved_dailyrain = dailyrain 
        outfile = open(filename,'wb')
        pickle.dump(saved_dailyrain,outfile)
        outfile.close()
        if loglevel == 2:
            syslog.syslog('Daily rain was not previously saved. Saved updated dailyrain %s' % dailyrain)
 
    rainTick = 0
 
    if loglevel == 2:
 
        #Print the results
        syslog.syslog('Timestamp:   %s' % datetime.datetime.now().strftime("%y-%m-%d %H:%M:%S"))
        syslog.syslog('Temperature: %s Celsius' % outtemp)
        syslog.syslog('Board Temp:  %s Celsius' % case_temp)
        syslog.syslog('Humidity:    %s percent' % outhumi)
        syslog.syslog('Pressure:    %s hpa' % absbaro)
        syslog.syslog('Wind Dir:    %s degrees' % winddir)
        syslog.syslog('Wind Speed:  %s km/h' % windspeed)
        syslog.syslog('Wind Gust:   %s km/h' % windgust)
        syslog.syslog('Rainfall:    %s mm' % rain)
        syslog.syslog('Daily Rain:  %s mm' % dailyrain)

    payload = {'outtemp' : outtemp, 'outhumi' : outhumi, 'absbaro' : absbaro, 'windspeed' : windspeed, 'winddir' : winddir, 'windgust' : windgust, 'dailyrain' : dailyrain, 'intemp' : case_temp} 

    try:
        r = requests.get(upload_url, params=payload)
        if loglevel >= 1:
            syslog.syslog(r.url)
            syslog.syslog(r.text)

    #Catch the exception if the connection fails
    except:
        syslog.syslog(r.url)
        syslog.syslog("connection failed")

Reply via email to