Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Weather Underground destination #165

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
3320eec
config and helpers created
sjefferson99 Mar 23, 2023
b699378
Working dummy wunderground destination
sjefferson99 Mar 25, 2023
36b6c3d
Working wunderground destination
sjefferson99 Mar 25, 2023
f4c5a3a
Documentation
sjefferson99 Mar 25, 2023
de48318
Add sea level pressure calculation
sjefferson99 Mar 20, 2023
024abaf
Reduce rounding error
sjefferson99 Mar 20, 2023
f7fa373
Return sea level in new reading key
sjefferson99 Mar 24, 2023
1b5cc66
Added sea level pressure to wunderground
sjefferson99 Mar 25, 2023
bd22b2c
Supported readings and typos
sjefferson99 Mar 25, 2023
140ab57
wunderground provisioning
sjefferson99 Mar 25, 2023
c4b0fab
Add rain per hour and day results
sjefferson99 Mar 29, 2023
a6665a9
Added rain per hour and day
sjefferson99 Mar 29, 2023
e91ca6b
Capture potential lost readings over midnight
sjefferson99 Mar 30, 2023
f7cc005
Add dewpoint calculation
sjefferson99 Mar 30, 2023
5d97202
Can't measure radiation inside Stevenson screen
sjefferson99 Mar 30, 2023
d988f87
Rain today adjusted to use local time
sjefferson99 Apr 1, 2023
80bf616
Updated documentation
sjefferson99 Apr 1, 2023
5513aa4
Documentation
sjefferson99 Apr 1, 2023
045dbdb
Documentation
sjefferson99 Apr 1, 2023
bfcc83d
Added request close
sjefferson99 Apr 11, 2023
03dc237
Add hard coded BST calculation for rain day
sjefferson99 Jul 13, 2023
2baf04e
Documentation update for uk_bst
sjefferson99 Aug 29, 2023
0210499
Log message improvement
sjefferson99 Aug 29, 2023
34504bb
Improved comments
sjefferson99 Aug 29, 2023
41ee5b6
Re add missed wifi_country from merge conflict
sjefferson99 Apr 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion documentation/boards/enviro-weather.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,22 @@ Enviro Weather is a super slimline all in one board for keeping a (weather) eye
|---|---|---|---|---|
|Temperature|`temperature`|celcius|°C|`22.11`|
|Humidity|`humidity`|percent|%|`55.42`|
|Dew Point|`dewpoint`|celcius|°C|`12.21`|
|Air Pressure|`pressure`|hectopascals|hPa|`997.16`|
|Adjusted Sea Level Air Pressure|`sea_level_pressure`|hectopascals|hPa|`1014.06`|
|Luminance|`luminance`|lux|lx|`35`|
|Rainfall|`rain`|millimetres|mm|`1.674`|
|Rainfall Average|`rain_per_second`|millimetres per second|mm/s|`1.674`|
|Rainfall Average Second|`rain_per_second`|millimetres per second|mm/s|`1.674`|
|Rainfall Average Hour|`rain_per_hour`|millimetres per hour|mm/h|`1.674`|
|Rainfall Today (local time)|`rain_today`|millimetres accumulated today|mm/s|`1.674`|
|Wind Direction|`wind_direction`|angle|°|`45`|
|Wind Speed|`wind_speed`|metres per second|m/s|`0.45`|
|Voltage|`voltage`|volts|V|`4.035`|

The rain today value is adjusted for DST in the UK by setting uk_bst = True in config.py
For static time zone offsets (not taking account of DST), modify the utc_offset value in config.py
The time zone offset value is ignored if uk_bst = True

## On-board devices

- BME280 temperature, pressure, humidity sensor. [View datasheet](https://www.bosch-sensortec.com/media/boschsensortec/downloads/datasheets/bst-bme280-ds002.pdf)
Expand Down
30 changes: 30 additions & 0 deletions documentation/destinations/wunderground.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# Weather Underground

Referred to as "wunderground in config and code", Weather Underground is an online service provided by IBM for uploading and viewing information from personal Weather Stations (PWS) and provides general weather forecasts and news.

You can sign up for a free account, configure a device and drop the credentials into the Enviro configuration to have the values appear on your own weather station dashboard that is also shared with the world.

## Setting up a Weather Underground account and device

1. Visit [Weather Underground](https://www.wunderground.com/)
2. Create an account by clicking [Join](https://www.wunderground.com/signup) in the navigation bar
3. Create your account using your email and a new password
4. Browse to your [devices](https://www.wunderground.com/member/devices) in your profile
5. Click Add New Device - Select "Raspberry Pi" as the device hardware and follow the wizard selecting appropriate values (if in doubt, go American units)
6. Credentials are displayed on the summary screen, but you can check these at any time on the device page

When you provision your device enter the station ID and station key and Enviro will automatically start sending data on the schedule you have requested.

Ensure you have enabled the sea level pressure option and provided accurate [elevation data](https://whatismyelevation.com/) if you want to return pressure data.

You can view the dashboard for your station by clicking the station name on the [devices](https://www.wunderground.com/member/devices) page.

## Supported readings
The following readings will be uploaded. Depending on board type, other readings may be collected, but not sent to Weather Underground and essentially dropped.
- Temperature
- Humidity
- Sea level pressure
- Wind speed
- Wind direction

There is potential to build logic to calculate rain per hour and convert luminance to solar radiation to capture all Enviro Weather sensor output, other boards may not be well suited to this destination.
142 changes: 93 additions & 49 deletions enviro/boards/weather.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import time, math, os
import time, math, os, config
from breakout_bme280 import BreakoutBME280
from breakout_ltr559 import BreakoutLTR559
from machine import Pin, PWM
from pimoroni import Analog
from enviro import i2c, activity_led
from enviro import i2c, activity_led, config
import enviro.helpers as helpers
from phew import logging
from enviro.constants import WAKE_REASON_RTC_ALARM, WAKE_REASON_BUTTON_PRESS
Expand All @@ -25,6 +25,28 @@
rain_pin = Pin(10, Pin.IN, Pin.PULL_DOWN)
last_rain_trigger = False

def log_rain():
# read the current rain entries
rain_entries = []
if helpers.file_exists("rain.txt"):
with open("rain.txt", "r") as rainfile:
rain_entries = rainfile.read().split("\n")

# add new entry
logging.info(f"> add new rain trigger at {helpers.datetime_string()}")
rain_entries.append(helpers.datetime_string())

# limit number of entries to 190 - each entry is 21 bytes including
# newline so this keeps the total rain.txt filesize just under one
# filesystem block (4096 bytes)
if len(rain_entries) > 190:
logging.info("Rain log file exceeded 190 entries and was truncated")
rain_entries = rain_entries[-190:]

# write out adjusted rain log
with open("rain.txt", "w") as rainfile:
rainfile.write("\n".join(rain_entries))

def startup(reason):
global last_rain_trigger
import wakeup
Expand All @@ -33,24 +55,7 @@ def startup(reason):
rain_sensor_trigger = wakeup.get_gpio_state() & (1 << 10)

if rain_sensor_trigger:
# read the current rain entries
rain_entries = []
if helpers.file_exists("rain.txt"):
with open("rain.txt", "r") as rainfile:
rain_entries = rainfile.read().split("\n")

# add new entry
logging.info(f"> add new rain trigger at {helpers.datetime_string()}")
rain_entries.append(helpers.datetime_string())

# limit number of entries to 190 - each entry is 21 bytes including
# newline so this keeps the total rain.txt filesize just under one
# filesystem block (4096 bytes)
rain_entries = rain_entries[-190:]

# write out adjusted rain log
with open("rain.txt", "w") as rainfile:
rainfile.write("\n".join(rain_entries))
log_rain()

last_rain_trigger = True

Expand All @@ -70,24 +75,7 @@ def check_trigger():
time.sleep(0.05)
activity_led(0)

# read the current rain entries
rain_entries = []
if helpers.file_exists("rain.txt"):
with open("rain.txt", "r") as rainfile:
rain_entries = rainfile.read().split("\n")

# add new entry
logging.info(f"> add new rain trigger at {helpers.datetime_string()}")
rain_entries.append(helpers.datetime_string())

# limit number of entries to 190 - each entry is 21 bytes including
# newline so this keeps the total rain.txt filesize just under one
# filesystem block (4096 bytes)
rain_entries = rain_entries[-190:]

# write out adjusted rain log
with open("rain.txt", "w") as rainfile:
rainfile.write("\n".join(rain_entries))
log_rain()

last_rain_trigger = rain_sensor_trigger

Expand Down Expand Up @@ -159,26 +147,65 @@ def wind_direction():
return closest_index * 45

def rainfall(seconds_since_last):
amount = 0
new_rain_entries = []
amount = 0 # rain since last reading
per_hour = 0
today = 0
offset = 0 # UTC offset hours

# configure offset variable for UK BST or timezone offset from config file
# and BST lookup function
if config.uk_bst == True:
if helpers.uk_bst():
offset = 1
elif config.utc_offset != 0:
offset += config.utc_offset

# determine current day number and timestamp
now = helpers.timestamp(helpers.datetime_string())
now_day = helpers.timestamp_day(helpers.datetime_string(), offset)
logging.info(f"> current day number is {now_day}")

# process the rain file data
if helpers.file_exists("rain.txt"):
with open("rain.txt", "r") as rainfile:
rain_entries = rainfile.read().split("\n")

# count how many rain ticks since the last reading
# populate latest, per second, today and last hour readings from rain log
# file, write new rain log file dropping any yesterday readings
for entry in rain_entries:
if entry:
ts = helpers.timestamp(entry)
tsday = helpers.timestamp_day(entry, config.utc_offset)
logging.info(f"> rain reading day number is {tsday}")
# populate amount with rain since the last reading
if now - ts < seconds_since_last:
amount += RAIN_MM_PER_TICK

os.remove("rain.txt")
# add any rain ticks from yesterday since the previous reading
# this will misallocate day totals, but will ensure the hourly total
# is correct without introducing complexity backdating yesterday and
# the error will be minimised with frequent readings
# TODO sum yesterday rain and generate a rain_today reading with
# 23:59:59 timestamp of yesterday
if tsday != now_day:
today += RAIN_MM_PER_TICK
# count how many rain ticks in the last hour
if now - ts < 3600:
per_hour += RAIN_MM_PER_TICK
# count how many rain ticks today and drop older entries for new file
if tsday == now_day:
today += RAIN_MM_PER_TICK
new_rain_entries.append(entry)

# write out new adjusted rain log
with open("rain.txt", "w") as newrainfile:
newrainfile.write("\n".join(new_rain_entries))

per_second = 0
if seconds_since_last > 0:
per_second = amount / seconds_since_last

return amount, per_second
return amount, per_second, per_hour, today

def get_sensor_readings(seconds_since_last, is_usb_power):
# bme280 returns the register contents immediately and then starts a new reading
Expand All @@ -188,16 +215,33 @@ def get_sensor_readings(seconds_since_last, is_usb_power):
bme280_data = bme280.read()

ltr_data = ltr559.get_reading()
rain, rain_per_second = rainfall(seconds_since_last)
rain, rain_per_second, rain_per_hour, rain_today = rainfall(seconds_since_last)

pressure = bme280_data[1] / 100.0
temperature = bme280_data[0]
humidity = bme280_data[2]

from ucollections import OrderedDict
return OrderedDict({
"temperature": round(bme280_data[0], 2),
"humidity": round(bme280_data[2], 2),
"pressure": round(bme280_data[1] / 100.0, 2),
readings = OrderedDict({
"temperature": round(temperature, 2),
"humidity": round(humidity, 2),
"pressure": round(pressure, 2),
"luminance": round(ltr_data[BreakoutLTR559.LUX], 2),
"wind_speed": wind_speed(),
"rain": rain,
"rain_per_second": rain_per_second,
"wind_direction": wind_direction()
"rain_per_hour": rain_per_hour,
"rain_today": rain_today,
"wind_direction": wind_direction(),
"dewpoint": round(helpers.calculate_dewpoint(temperature, humidity), 2)
})

# Add adjusted pressure to calculated sea level value if set to in config
if config.sea_level_pressure:
logging.info(f" - recorded temperature: {temperature}")
logging.info(f" - recorded pressure: {pressure}")
sea_level_pressure = round(helpers.get_sea_level_pressure(pressure, temperature, config.height_above_sea_level), 2)
logging.info(f" - calculated mean sea level pressure: {sea_level_pressure}")
readings["sea_level_pressure"] = round(sea_level_pressure, 2)

return readings
40 changes: 39 additions & 1 deletion enviro/config_defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
from phew import logging

DEFAULT_USB_POWER_TEMPERATURE_OFFSET = 4.5
DEFAULT_UTC_OFFSET = 0
DEFAULT_UK_BST = True


def add_missing_config_settings():
Expand All @@ -24,5 +26,41 @@ def add_missing_config_settings():
warn_missing_config_setting("wifi_country")
config.wifi_country = "GB"

try:
config.wunderground_id
except AttributeError:
warn_missing_config_setting("wunderground_id")
config.wunderground_id = None

try:
config.wunderground_key
except AttributeError:
warn_missing_config_setting("wunderground_key")
config.wunderground_key = None

try:
config.sea_level_pressure
except AttributeError:
warn_missing_config_setting("sea_level_pressure")
config.sea_level_pressure = False

try:
config.height_above_sea_level
except AttributeError:
warn_missing_config_setting("height_above_sea_level")
config.height_above_sea_level = 0

try:
config.uk_bst
except AttributeError:
warn_missing_config_setting("uk_bst")
config.uk_bst = DEFAULT_UK_BST

try:
config.utc_offset
except AttributeError:
warn_missing_config_setting("utc_offset")
config.utc_offset = DEFAULT_UTC_OFFSET

def warn_missing_config_setting(setting):
logging.warn(f"> config setting '{setting}' missing, please add it to config.py")
logging.warn(f"> config setting '{setting}' missing, please add it to config.py")
18 changes: 17 additions & 1 deletion enviro/config_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,20 @@
wifi_password = None
wifi_country = "GB"

# Adjust daily rain day for UK BST
uk_bst = True

# For local time corrections to daily rain logging other than BST
# Ignored if uk_bst = True
utc_offset = 0

# how often to wake up and take a reading (in minutes)
reading_frequency = 15

# how often to trigger a resync of the onboard RTC (in hours)
resync_frequency = 168

# where to upload to ("http", "mqtt", "adafruit_io", "influxdb")
# where to upload to ("http", "mqtt", "adafruit_io", "influxdb", "wunderground")
destination = None

# how often to upload data (number of cached readings)
Expand Down Expand Up @@ -47,6 +54,10 @@
influxdb_token = None
influxdb_bucket = None

# weather underground settings
wunderground_id = None
wunderground_key = None

# grow specific settings
auto_water = False
moisture_target_a = 50
Expand All @@ -55,3 +66,8 @@

# compensate for usb power
usb_power_temperature_offset = 4.5

# sea level pressure conversion (adjusts measured pressure output for mean sea level value)
sea_level_pressure = False
# height in metres
height_above_sea_level = 0
Loading