Git initial commit
This commit is contained in:
commit
baeb1ab185
32
.gitignore
vendored
Executable file
32
.gitignore
vendored
Executable file
@ -0,0 +1,32 @@
|
|||||||
|
# Prerequisites
|
||||||
|
*.d
|
||||||
|
|
||||||
|
# Compiled Object files
|
||||||
|
*.slo
|
||||||
|
*.lo
|
||||||
|
*.o
|
||||||
|
*.obj
|
||||||
|
|
||||||
|
# Precompiled Headers
|
||||||
|
*.gch
|
||||||
|
*.pch
|
||||||
|
|
||||||
|
# Compiled Dynamic libraries
|
||||||
|
*.so
|
||||||
|
*.dylib
|
||||||
|
*.dll
|
||||||
|
|
||||||
|
# Fortran module files
|
||||||
|
*.mod
|
||||||
|
*.smod
|
||||||
|
|
||||||
|
# Compiled Static libraries
|
||||||
|
*.lai
|
||||||
|
*.la
|
||||||
|
*.a
|
||||||
|
*.lib
|
||||||
|
|
||||||
|
# Executables
|
||||||
|
*.exe
|
||||||
|
*.out
|
||||||
|
*.app
|
9
Info.txt
Executable file
9
Info.txt
Executable file
@ -0,0 +1,9 @@
|
|||||||
|
Measuring soil moisture requires to translate frequency to a certain water level.
|
||||||
|
The easiest one is to set minimum and maximum of water to a certain frequency.
|
||||||
|
|
||||||
|
This was done experimental; in future, there will be a calibration function, that tracks the optimal values aswell.
|
||||||
|
|
||||||
|
Currently we use:
|
||||||
|
|
||||||
|
- "Air with 40% humidity" ~ +-9350 HZ
|
||||||
|
- "Sensor complete in water" ~ +- 800 HZ
|
21
LICENSE
Executable file
21
LICENSE
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2021 Lars Hahn
|
||||||
|
|
||||||
|
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.
|
18
README.md
Executable file
18
README.md
Executable file
@ -0,0 +1,18 @@
|
|||||||
|
# green-environment
|
||||||
|
|
||||||
|
The intention of this project is to setup an application which can be used within greenhouses, that monitors environmental factors and partially is able to controle these.
|
||||||
|
|
||||||
|
The core of the project is to have a set of plants, where soil moisture sensors are integrated, next to sensors for air pressure, humidity, light intensity and temperature (gas meter are an interesseting aspect for the future aswell!).
|
||||||
|
|
||||||
|
With this application we then can for instance controll a pump to control irrigation.
|
||||||
|
|
||||||
|
The first step is to implement a short commandline application in python, followed by a C++ application, which is/are then extended with a GUI and a live plotting of current values (temp etc.).
|
||||||
|
|
||||||
|
|
||||||
|
The hardware base will be a raspberry pi for sensor access.
|
||||||
|
The following sensors will be used:
|
||||||
|
- Gies-O-Mat soil moisture sensor from ramser-elektro.at is used
|
||||||
|
- TSL2591 Lux Sensor (or alternative) will be used for light parameter
|
||||||
|
- BME280 Pressure, Temperature, Humidity Sensor for air related parameters
|
||||||
|
- DS18B20 Waterproof temperature sensor for plant soil temperature
|
||||||
|
|
0
bin/.gitkeep
Executable file
0
bin/.gitkeep
Executable file
2
lib/python3/green_environment/__init__.py
Executable file
2
lib/python3/green_environment/__init__.py
Executable file
@ -0,0 +1,2 @@
|
|||||||
|
from giesomat import GiesOMat
|
||||||
|
from ds18b20 import DS18B20
|
372
lib/python3/green_environment/ds18b20.py
Executable file
372
lib/python3/green_environment/ds18b20.py
Executable file
@ -0,0 +1,372 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
This module provides a class to interact with the DS18B20 temperature
|
||||||
|
sensor and is considered mostly to be used on a Raspberry Pi. With
|
||||||
|
aprorpiate naming this class might be used for other SoC like Arduino,
|
||||||
|
Genuino etc.
|
||||||
|
|
||||||
|
This module can also be used as a standalone script to retrieve values from.
|
||||||
|
attached sensors.
|
||||||
|
|
||||||
|
Classes: DS18B20
|
||||||
|
Functions: main
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import argparse
|
||||||
|
import time
|
||||||
|
|
||||||
|
os.system('modprobe w1-gpio')
|
||||||
|
os.system('modprobe w1-therm')
|
||||||
|
|
||||||
|
class DS18B20:
|
||||||
|
"""
|
||||||
|
This class provides a set of functions and methods that can be used to
|
||||||
|
interact with the DS18B20 sensor. As values can be set/adjusted during
|
||||||
|
runtime, there is no need to instantiate a new object for every config
|
||||||
|
change. Multiple sensors can be handled at once by passing a list of
|
||||||
|
devices or using None (take all available devices).
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
default_functor(values, **kwargs)
|
||||||
|
__init__(device, scale, idle_time)
|
||||||
|
set_devices()
|
||||||
|
devices()
|
||||||
|
set_device_path()
|
||||||
|
device_path()
|
||||||
|
set_scale()
|
||||||
|
scale()
|
||||||
|
set_idle_time()
|
||||||
|
idle_time()
|
||||||
|
__to_celsius
|
||||||
|
__to_kelvin
|
||||||
|
__to_fahrenheit
|
||||||
|
get(iteration)
|
||||||
|
run(iteration, functor, **kwargs)
|
||||||
|
run_endless(iteration, functor, **kwargs)
|
||||||
|
"""
|
||||||
|
def default_functor(values: "list of ints" or int, **kwargs):
|
||||||
|
"""
|
||||||
|
An example of how functions can be passed to the run function,
|
||||||
|
to make use of a value handling (e.g. printing values).
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
values (list of ints) -- the measured values for one run.
|
||||||
|
Args:
|
||||||
|
**kwargs -- arguments that can be evaluated in another function.
|
||||||
|
"""
|
||||||
|
if not isinstance(values, list):
|
||||||
|
print(values, **kwargs)
|
||||||
|
else:
|
||||||
|
print("\t".join(str(value) for value in values), **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def __init__(self, device, scale: str = "C", idle_time: int = 2):
|
||||||
|
"""
|
||||||
|
Constructor to instantiate an object, that is able to handle multiple
|
||||||
|
devices at once.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
device (str, list of str) -- The 1-wire devices that should be used.
|
||||||
|
scale (str, optional) -- The temperature scalce (K,F,C) to be used
|
||||||
|
idle_time (int, optional) -- The idle time between two measurements.
|
||||||
|
"""
|
||||||
|
self.set_scale(scale)
|
||||||
|
self._idle_time = idle_time
|
||||||
|
self._device_path = "/sys/bus/w1/devices"
|
||||||
|
dev = [device] if not isinstance(device,list) else device
|
||||||
|
self.set_devices(dev)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def set_devices(self, devices: list=None):
|
||||||
|
"""
|
||||||
|
This functions loads the required 1-wire devices.
|
||||||
|
If no device list is provided, all available devices will be used.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
devices (list of str) -- the devices to be used; leave empty
|
||||||
|
to use all devices
|
||||||
|
|
||||||
|
"""
|
||||||
|
device_list = [devices] if not isinstance(devices, list) else devices
|
||||||
|
# filter available 1-wire devices #
|
||||||
|
one_wire_devices = [
|
||||||
|
dev.name for dev in os.scandir(self._device_path)
|
||||||
|
if dev.is_dir() and dev.name != "w1_bus_master1"
|
||||||
|
]
|
||||||
|
|
||||||
|
# filter for active 1-wire devices #
|
||||||
|
self._devices = [
|
||||||
|
dev for dev in one_wire_devices
|
||||||
|
if "w1_slave" in (
|
||||||
|
item.name for item in os.scandir("{}/{}".format(self._device_path, dev))
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
||||||
|
# reduce to chosen, active devices #
|
||||||
|
if device_list != [None]:
|
||||||
|
self._devices = [
|
||||||
|
device
|
||||||
|
for device in self._devices
|
||||||
|
if device in device_list
|
||||||
|
]
|
||||||
|
if len(self._devices) != len(device_list):
|
||||||
|
missing_devices = [
|
||||||
|
dev for dev in devices if dev not in self._devices
|
||||||
|
]
|
||||||
|
raise FileNotFoundError(
|
||||||
|
"Provided devices [{}] cannot be found! Aborting.".format(
|
||||||
|
", ".join(missing_devices)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if not self._devices:
|
||||||
|
raise FileNotFoundError("No 1-wire device found!")
|
||||||
|
|
||||||
|
|
||||||
|
def devices(self):
|
||||||
|
"""
|
||||||
|
The function to get (by return) the current device list in use.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(list) -- The device list.
|
||||||
|
"""
|
||||||
|
return list(self._devices)
|
||||||
|
|
||||||
|
|
||||||
|
def set_device_path(self, device_path: str):
|
||||||
|
"""
|
||||||
|
Sets the device path, were 1-wire devices can be found.
|
||||||
|
Usually it is '/sys/bus/w1/devices'
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
device_path (str) -- The device path, e.g. /sys/bus/w1/devices
|
||||||
|
"""
|
||||||
|
self._device_path = device_path
|
||||||
|
|
||||||
|
|
||||||
|
def device_path(self):
|
||||||
|
"""
|
||||||
|
The function to get (by return) the current device_path, where
|
||||||
|
1-wire devices should be found.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(str) -- The device_path as a string.
|
||||||
|
"""
|
||||||
|
return self._device_path
|
||||||
|
|
||||||
|
|
||||||
|
def set_scale(self, scale: str):
|
||||||
|
"""
|
||||||
|
Sets the temperature scale, that should be used; it is either
|
||||||
|
'K', 'F' or 'C'; relates to Kevlin, Fahrenheit or Celsius.
|
||||||
|
Returned values are to be read with that scale; default is C.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
scale (str) -- The temperature scale: 'K', 'F' or 'C'.
|
||||||
|
"""
|
||||||
|
if scale not in ("K", "F", "C"):
|
||||||
|
raise ValueError(
|
||||||
|
"Unknown scale type '{}'! Needs to be K, F or C!".format(scale)
|
||||||
|
)
|
||||||
|
self._scale = scale
|
||||||
|
if self._scale == "C":
|
||||||
|
self._translator = self.__to_celsius
|
||||||
|
elif self._scale == "F":
|
||||||
|
# Fahrenheit temperature scale #
|
||||||
|
self._translator = self.__to_fahrenheit
|
||||||
|
else:
|
||||||
|
# Kelvin temperature scale #
|
||||||
|
self._translator = self.__to_kelvin
|
||||||
|
|
||||||
|
|
||||||
|
def scale(self):
|
||||||
|
"""
|
||||||
|
The function to get (by return) the current used temperature scale.
|
||||||
|
This is either 'K', 'F' or 'C'.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(str) -- The uses temperature scale as a string.
|
||||||
|
"""
|
||||||
|
return self._scale
|
||||||
|
|
||||||
|
|
||||||
|
def set_idle_time(self, idle_time: int):
|
||||||
|
"""
|
||||||
|
Sets the idle time that is used in between two
|
||||||
|
measurements when using run(...) or run_endless(...)
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
idle_time (int) -- The idle time.
|
||||||
|
"""
|
||||||
|
self._idle_time = idle_time
|
||||||
|
|
||||||
|
|
||||||
|
def idle_time(self):
|
||||||
|
"""
|
||||||
|
The function to get (by return) the current used idle time.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(int) -- The currently used idle time.
|
||||||
|
"""
|
||||||
|
return self._idle_time
|
||||||
|
|
||||||
|
|
||||||
|
def __to_celsius(self, value: int):
|
||||||
|
"""
|
||||||
|
Transforms measured value into Celcius temperature scale.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
value (int) -- A measured value that should be transformed.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(int) -- The transformed input value.
|
||||||
|
"""
|
||||||
|
return value / 1000.0
|
||||||
|
|
||||||
|
|
||||||
|
def __to_kelvin(self, value: int):
|
||||||
|
"""
|
||||||
|
Transforms measured value into Kelvin temperature scale.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
value (int) -- A measured value that should be transformed.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(int) -- The transformed input value.
|
||||||
|
"""
|
||||||
|
return (value/1000) + 273.15
|
||||||
|
|
||||||
|
|
||||||
|
def __to_fahrenheit(self, value: int):
|
||||||
|
"""
|
||||||
|
Transforms measured value into Fahrenheit temperature scale.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
value (int) -- A measured value that should be transformed.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(int) -- The transformed input value.
|
||||||
|
"""
|
||||||
|
return (value/1000)*(9/5) + 32
|
||||||
|
|
||||||
|
|
||||||
|
def get(self, iteration: int = 1):
|
||||||
|
"""
|
||||||
|
Function to get a certain amount of measured values; there is no
|
||||||
|
on-line handling. Values will be measured and returned.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
iteration (int, optional) -- Measured values amount. Defaults to 1.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(list of list of ints or list of ints) -- The measured values
|
||||||
|
"""
|
||||||
|
iteration_values = []
|
||||||
|
for _ in range(iteration):
|
||||||
|
current_temp = []
|
||||||
|
for device in self._devices:
|
||||||
|
one_wire_file = "{}/{}/w1_slave".format(self._device_path, device)
|
||||||
|
with open(one_wire_file, "r") as bytestream:
|
||||||
|
data = [
|
||||||
|
item.rstrip().split(" ")[-1] for item in bytestream.readlines()
|
||||||
|
]
|
||||||
|
if data[0] == "YES":
|
||||||
|
current_temp.append(self._translator(int(data[1][2:])))
|
||||||
|
else:
|
||||||
|
current_temp.append(None)
|
||||||
|
iteration_values.append(current_temp)
|
||||||
|
time.sleep(self._idle_time)
|
||||||
|
if iteration == 1:
|
||||||
|
return iteration_values[0]
|
||||||
|
return iteration_values
|
||||||
|
|
||||||
|
|
||||||
|
def run(self, iteration: int = 1, functor=default_functor, **kwargs):
|
||||||
|
"""
|
||||||
|
Function to measure a certain amount of values and evaluate them directly.
|
||||||
|
Evaluation can be a print function or a self-defined function.
|
||||||
|
Options can be passed with **kwargs.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
iteration (int, optional) -- Number of measurements to be done.
|
||||||
|
Defaults to 1.
|
||||||
|
functor (function_ptr, optional) -- An evaluationfunction.
|
||||||
|
Defaults to default_functor.
|
||||||
|
Args:
|
||||||
|
**kwargs -- arguments that can be evaluated in another function.
|
||||||
|
"""
|
||||||
|
while iteration != 0:
|
||||||
|
if iteration > 0:
|
||||||
|
iteration -= 1
|
||||||
|
current_temp = []
|
||||||
|
for device in self._devices:
|
||||||
|
one_wire_file = "{}/{}/w1_slave".format(self._device_path, device)
|
||||||
|
with open(one_wire_file, "r") as bytestream:
|
||||||
|
data = [
|
||||||
|
item.rstrip().split(" ")[-1] for item in bytestream.readlines()
|
||||||
|
]
|
||||||
|
if data[0] == "YES":
|
||||||
|
current_temp.append(self._translator(int(data[1][2:])))
|
||||||
|
else:
|
||||||
|
current_temp.append(None)
|
||||||
|
functor(current_temp, **kwargs)
|
||||||
|
time.sleep(self._idle_time)
|
||||||
|
|
||||||
|
|
||||||
|
def run_endless(self, functor=default_functor, **kwargs):
|
||||||
|
"""
|
||||||
|
Function to permanently measure values and evaluate them directly.
|
||||||
|
Evaluation can be a print function or a self-defined function.
|
||||||
|
Options can be passed with **kwargs.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
functor (function_ptr, optional) -- An evaluationfunction.
|
||||||
|
Defaults to default_functor.
|
||||||
|
Args:
|
||||||
|
**kwargs -- arguments that can be evaluated in another function.
|
||||||
|
"""
|
||||||
|
self.run(iteration=-1, functor=functor, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""
|
||||||
|
A main function that is used, when this module is used as a stand-alone script.
|
||||||
|
Arguments can be passed and it will simply print results to std-out.
|
||||||
|
"""
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="A short programm to print values from DS18B20 sensor."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-d", metavar="D", nargs="+", type=str, required=True,
|
||||||
|
help="The devices 1-wire, that should be used to measure temperature."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-t", metavar="T", default=2, type=int, required=False,
|
||||||
|
help="Set idle time (break between measurements); default t = 2."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-c", metavar="C", default="C", type=str, required=False,
|
||||||
|
help="The tempearute scale to use (K,F,C); default c = C."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-i", metavar="I", default=10, type=int, required=False,
|
||||||
|
help="Number of iterations to get a value; use -1 for infinity."
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
devices = args.d
|
||||||
|
iterations = -1 if args.i < 0 else args.i
|
||||||
|
idle_time = args.t
|
||||||
|
scale = args.c
|
||||||
|
|
||||||
|
connector = DS18B20(
|
||||||
|
device=devices,
|
||||||
|
scale=scale,
|
||||||
|
idle_time=idle_time
|
||||||
|
)
|
||||||
|
connector.run(iterations)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# execute only if run as a script
|
||||||
|
main()
|
289
lib/python3/green_environment/giesomat.py
Executable file
289
lib/python3/green_environment/giesomat.py
Executable file
@ -0,0 +1,289 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
This module provides a class to interact with the Gies-O-Mat soil moisture
|
||||||
|
sensor from ramser-elektro.at and is considered mostly to be used on a
|
||||||
|
Raspberry Pi. With aprorpiate naming and if pigpio is also available,
|
||||||
|
this class might be used for other SoC like Arduino, Genuino etc.
|
||||||
|
|
||||||
|
This module can also be used as a standalone script to retrieve values from.
|
||||||
|
attached sensors.
|
||||||
|
|
||||||
|
Classes: GiesOMat
|
||||||
|
Functions: main
|
||||||
|
"""
|
||||||
|
import argparse
|
||||||
|
import time
|
||||||
|
import pigpio
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class GiesOMat:
|
||||||
|
"""
|
||||||
|
This class provides a set of functions and methods that can be used to
|
||||||
|
interact with the Gies-O-Mat sensor. As values can be set/adjusted during
|
||||||
|
runtime, there is no need to instantiate a new object for every config
|
||||||
|
change. Multiple sensors can be handled at once by passing a list of
|
||||||
|
GPIO pins.
|
||||||
|
|
||||||
|
Methods:
|
||||||
|
default_functor(values, **kwargs)
|
||||||
|
__init__
|
||||||
|
set_gpio(gpio)
|
||||||
|
gpio()
|
||||||
|
set_pulse(pulse)
|
||||||
|
pulse()
|
||||||
|
set_sample_rate(sample_rate)
|
||||||
|
sample_rate()
|
||||||
|
set_callback(call_back_id)
|
||||||
|
callback()
|
||||||
|
reset()
|
||||||
|
get(iteration)
|
||||||
|
run(iteration, functor, **kwargs)
|
||||||
|
run_endless(iteration, functor, **kwargs)
|
||||||
|
"""
|
||||||
|
def default_functor(values: list or int, **kwargs):
|
||||||
|
"""
|
||||||
|
An example of how functions can be passed to the run function,
|
||||||
|
to make use of a value handling (e.g. printing values).
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
values (list of ints) -- the measured values for one run.
|
||||||
|
Args:
|
||||||
|
**kwargs -- arguments that can be evaluated in another function
|
||||||
|
"""
|
||||||
|
if not isinstance(values, list):
|
||||||
|
print(values, **kwargs)
|
||||||
|
else:
|
||||||
|
print("\t".join(str(value) for value in values), **kwargs)
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self, gpio, pulse=20, sample_rate=5,
|
||||||
|
call_back_id=pigpio.RISING_EDGE):
|
||||||
|
"""
|
||||||
|
Constructor to instantiate an object, that is able to handle multiple
|
||||||
|
sensors at once.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
gpio (int, list of ints) -- GPIO pins that are connected to 'OUT'
|
||||||
|
pulse (int, optional) -- The time for the charging wave. Defaults to 20.
|
||||||
|
sample_rate (int, optional) -- Time span how long to count switches. Defaults to 5.
|
||||||
|
call_back_id (int, optional) -- Callback id. Defaults to pigpio.RISING_EDGE.
|
||||||
|
"""
|
||||||
|
self.set_gpio(gpio)
|
||||||
|
self.set_pulse(pulse)
|
||||||
|
self.set_sample_rate(sample_rate)
|
||||||
|
self.set_callback(call_back_id)
|
||||||
|
self._pin_mask = 0
|
||||||
|
self.reset()
|
||||||
|
|
||||||
|
def set_gpio(self, gpio: list or int):
|
||||||
|
"""
|
||||||
|
The function allows to set or update the GPIO pin list of a GiesOMat
|
||||||
|
instance, so that pins can be changed/updated.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
gpio (int, list of ints) -- Sensor pins that are connected to "OUT".
|
||||||
|
"""
|
||||||
|
self._gpio = gpio if isinstance(gpio, list) else [gpio]
|
||||||
|
|
||||||
|
def gpio(self):
|
||||||
|
"""
|
||||||
|
The function to get (by return) the current list of used GPIO pins
|
||||||
|
where data is taken from.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(list of ints) -- Returns the list of used GPIO pins.
|
||||||
|
"""
|
||||||
|
return [gpio_pin for gpio_pin in self._gpio]
|
||||||
|
|
||||||
|
def set_pulse(self, pulse: int):
|
||||||
|
"""
|
||||||
|
Sets the pulse value (in µs) to the instance and on runtime.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
pulse (int) -- The pulse value in µs.
|
||||||
|
"""
|
||||||
|
self._pulse = pulse
|
||||||
|
|
||||||
|
def pulse(self):
|
||||||
|
"""
|
||||||
|
The function to get (by return) the current pulse value.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(int) -- The currently used pulse value.
|
||||||
|
"""
|
||||||
|
return self._pulse
|
||||||
|
|
||||||
|
def set_sample_rate(self, sample_rate: int or float):
|
||||||
|
"""
|
||||||
|
Sets the sample_rate value (in deciseconds [10^-1 s])to the instance
|
||||||
|
and on runtime.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
sample_rate (int) -- The sample_rate value in deciseconds.
|
||||||
|
"""
|
||||||
|
self._sample_rate = sample_rate
|
||||||
|
|
||||||
|
def sample_rate(self):
|
||||||
|
"""
|
||||||
|
The function to get (by return) the current sample_rate value.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(int) -- The currently used sample_rate value.
|
||||||
|
"""
|
||||||
|
return self._sample_rate
|
||||||
|
|
||||||
|
def set_callback(self, call_back_id: int):
|
||||||
|
"""
|
||||||
|
Sets the used callback trigger (when to count, rising, falling switch
|
||||||
|
point).
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
call_back_id (int) -- The callback id, e.g. pigpio.RISING_EDGE.
|
||||||
|
"""
|
||||||
|
self._call_back_id = call_back_id
|
||||||
|
|
||||||
|
def callback(self):
|
||||||
|
"""
|
||||||
|
The function to get (by return) the current callback_id value.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(int) -- The callback_id, please relate to e.g. pigpio.RISING_EDGE.
|
||||||
|
"""
|
||||||
|
return self._call_back_id
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
"""
|
||||||
|
This functions resets all necessary runtime variables, so that after
|
||||||
|
a configuration change, everything is correctly loaded.
|
||||||
|
"""
|
||||||
|
self._pi = pigpio.pi()
|
||||||
|
self._pi.wave_clear()
|
||||||
|
for gpio_pin in self._gpio:
|
||||||
|
self._pin_mask |= 1 << gpio_pin
|
||||||
|
self._pi.set_mode(gpio_pin, pigpio.INPUT)
|
||||||
|
self._pulse_gpio = [
|
||||||
|
pigpio.pulse(self._pin_mask, 0, self._pulse),
|
||||||
|
pigpio.pulse(0, self._pin_mask, self._pulse)
|
||||||
|
]
|
||||||
|
self._pi.wave_add_generic(self._pulse_gpio)
|
||||||
|
self._wave_id = self._pi.wave_create()
|
||||||
|
self._call_backs = [
|
||||||
|
self._pi.callback(
|
||||||
|
gpio_pin, self._call_back_id
|
||||||
|
) for gpio_pin in self._gpio
|
||||||
|
]
|
||||||
|
for callback in self._call_backs:
|
||||||
|
callback.reset_tally()
|
||||||
|
|
||||||
|
def get(self, iteration: int = 1):
|
||||||
|
"""
|
||||||
|
Function to get a certain amount of measured values; there is no
|
||||||
|
on-line handling. Values will be measured and returned.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
iteration (int, optional) -- Measured values amount. Defaults to 1.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
(list of list of ints or list of ints) -- The measured values
|
||||||
|
"""
|
||||||
|
# initialise/reset once to have values with beginning!
|
||||||
|
for call in self._call_backs:
|
||||||
|
call.reset_tally()
|
||||||
|
time.sleep(0.1 * self._sample_rate)
|
||||||
|
iteration_values = []
|
||||||
|
for _ in range(iteration):
|
||||||
|
values = [call.tally() for call in self._call_backs]
|
||||||
|
iteration_values.append(values)
|
||||||
|
for call in self._call_backs:
|
||||||
|
call.reset_tally()
|
||||||
|
time.sleep(0.1 * self._sample_rate)
|
||||||
|
if iteration == 1:
|
||||||
|
return iteration_values[0]
|
||||||
|
return iteration_values
|
||||||
|
|
||||||
|
def run(self, iteration: int = 1, functor=default_functor, **kwargs):
|
||||||
|
"""
|
||||||
|
Function to measure a certain amount of values and evaluate them directly.
|
||||||
|
Evaluation can be a print function or a self-defined function.
|
||||||
|
Options can be passed with **kwargs.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
iteration (int, optional) -- Number of measurements to be done.
|
||||||
|
Defaults to 1.
|
||||||
|
functor (function_ptr, optional) -- An evaluationfunction.
|
||||||
|
Defaults to default_functor.
|
||||||
|
Args:
|
||||||
|
**kwargs -- arguments that can be evaluated in another function.
|
||||||
|
"""
|
||||||
|
# initialise/reset once to have values with beginning!
|
||||||
|
for call in self._call_backs:
|
||||||
|
call.reset_tally()
|
||||||
|
time.sleep(0.1 * self._sample_rate)
|
||||||
|
while iteration != 0:
|
||||||
|
if iteration > 0:
|
||||||
|
iteration -= 1
|
||||||
|
values = [call.tally() for call in self._call_backs]
|
||||||
|
functor(values, **kwargs)
|
||||||
|
for call in self._call_backs:
|
||||||
|
call.reset_tally()
|
||||||
|
time.sleep(0.1 * self._sample_rate)
|
||||||
|
|
||||||
|
def run_endless(self, functor=default_functor, **kwargs):
|
||||||
|
"""
|
||||||
|
Function to permanently measure values and evaluate them directly.
|
||||||
|
Evaluation can be a print function or a self-defined function.
|
||||||
|
Options can be passed with **kwargs.
|
||||||
|
|
||||||
|
Keyword arguments:
|
||||||
|
functor (function_ptr, optional) -- An evaluationfunction.
|
||||||
|
Defaults to default_functor.
|
||||||
|
Args:
|
||||||
|
**kwargs -- arguments that can be evaluated in another function.
|
||||||
|
"""
|
||||||
|
self.run(iteration=-1, functor=functor, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""
|
||||||
|
A main function that is used, when this module is used as a stand-alone script.
|
||||||
|
Arguments can be passed and it will simply print results to std-out.
|
||||||
|
"""
|
||||||
|
parser = argparse.ArgumentParser(
|
||||||
|
description="A short programm to print values from Gies-O-Mat sensor."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-g", metavar="G", nargs="+", type=int, required=True,
|
||||||
|
help="GPIO pin number(s), where the OUT sensor(s) pin is/are attached to."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-p", metavar="P", default=20, type=int, required=False,
|
||||||
|
help="Set Pulse to P µs, default p = 20µs."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-s", metavar="S", default=5, type=int, required=False,
|
||||||
|
help="Set sample rate to S deciseconds [10^-1 s]; default s = 5."
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"-i", metavar="I", default=10, type=int, required=False,
|
||||||
|
help="Number of iterations to get a value; use -1 for infinity."
|
||||||
|
)
|
||||||
|
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
gpio_pins = args.g
|
||||||
|
iterations = -1 if args.i < 0 else args.i
|
||||||
|
pulse = args.p
|
||||||
|
sample_rate = args.s
|
||||||
|
|
||||||
|
connector = GiesOMat(
|
||||||
|
gpio=gpio_pins,
|
||||||
|
pulse=pulse,
|
||||||
|
sample_rate=sample_rate,
|
||||||
|
)
|
||||||
|
connector.run(iterations)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
# execute only if run as a script
|
||||||
|
main()
|
21
setup.py
Executable file
21
setup.py
Executable file
@ -0,0 +1,21 @@
|
|||||||
|
import setuptools
|
||||||
|
|
||||||
|
with open("README.md", "r") as file:
|
||||||
|
program_description = file.read()
|
||||||
|
|
||||||
|
setuptools.setup(
|
||||||
|
name="green_environment",
|
||||||
|
version="0.0.0",
|
||||||
|
author="Lars Hahn",
|
||||||
|
author_email="lhahn@data-learning.de",
|
||||||
|
description="Package to setup an application and libraries concerning environmental sensors and plant irrigation",
|
||||||
|
long_description=program_description,
|
||||||
|
long_description_content_type="text/markdown",
|
||||||
|
packages=setuptools.find_packages(),
|
||||||
|
classifiers=[
|
||||||
|
"Programming Language :: Python :: 3",
|
||||||
|
"License :: OSI Approved :: MIT License",
|
||||||
|
"Operating System :: OS Independent",
|
||||||
|
],
|
||||||
|
python_requires='>=3.7',
|
||||||
|
)
|
0
src/.gitkeep
Executable file
0
src/.gitkeep
Executable file
Loading…
Reference in New Issue
Block a user