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 rain per hour and day results #169

Open
wants to merge 8 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 7 additions & 1 deletion documentation/boards/enviro-weather.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,17 @@ Enviro Weather is a super slimline all in one board for keeping a (weather) eye
|Air Pressure|`pressure`|hectopascals|hPa|`997.16`|
|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
115 changes: 72 additions & 43 deletions enviro/boards/weather.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import time, math, os
import time, math, os, config
from breakout_bme280 import BreakoutBME280
from breakout_ltr559 import BreakoutLTR559
from machine import Pin, PWM
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,7 +215,7 @@ 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)

from ucollections import OrderedDict
return OrderedDict({
Expand All @@ -199,5 +226,7 @@ def get_sensor_readings(seconds_since_last, is_usb_power):
"wind_speed": wind_speed(),
"rain": rain,
"rain_per_second": rain_per_second,
"rain_per_hour": rain_per_hour,
"rain_today": rain_today,
"wind_direction": wind_direction()
})
14 changes: 14 additions & 0 deletions 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 @@ -23,6 +25,18 @@ def add_missing_config_settings():
except AttributeError:
warn_missing_config_setting("wifi_country")
config.wifi_country = "GB"

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")
7 changes: 7 additions & 0 deletions enviro/config_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@
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

Expand Down
38 changes: 37 additions & 1 deletion enviro/helpers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from enviro.constants import *
import machine, math, os, time
import machine, math, os, time, utime
from phew import logging

# miscellany
# ===========================================================================
Expand All @@ -24,6 +25,41 @@ def timestamp(dt):
second = int(dt[17:19])
return time.mktime((year, month, day, hour, minute, second, 0, 0))

def uk_bst():
# Return True if in UK BST - manually update bst_timestamps {} as needed
dt = datetime_string()
year = int(dt[0:4])
ts = timestamp(dt)
bst = False

bst_timestamps = {
2023: {"start": 1679792400, "end": 1698541200},
2024: {"start": 1711846800, "end": 1729990800},
2025: {"start": 1743296400, "end": 1761440400},
2026: {"start": 1774746000, "end": 1792890000},
2027: {"start": 1806195600, "end": 1824944400},
2028: {"start": 1837645200, "end": 1856394000},
2029: {"start": 1869094800, "end": 1887843600},
2030: {"start": 1901149200, "end": 1919293200}
}

if year in bst_timestamps:
if bst_timestamps[year]["start"] < ts and bst_timestamps[year]["end"] > ts:
bst = True
else:
logging.warn(f"> Current year is not in BST lookup dictionary: {year}")
return bst


# Return the day number of your timestamp string accommodating UTC offsets
def timestamp_day(dt, offset_hours):
# Bounce via timestamp to properly calculate hours change
time = timestamp(dt)
time = time + (offset_hours * 3600)
dt = utime.localtime(time)
day = int(dt[2])
return day

def uid():
return "{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}{:02x}".format(*machine.unique_id())

Expand Down