From e0a2ab9fd793724aa8944c933ba7d5d2e468b936 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maik=20Mu=CC=88ller?= Date: Thu, 25 Apr 2024 14:44:04 +0200 Subject: [PATCH 1/9] [WIP] moisture sensor working so far --- REDME.md | 3 +++ grow_system.py | 24 +++++++++++++++++++ main.py | 31 +++++++++++++++++-------- moisture_sensor.py | 21 +++++++++++++++++ notes/code_sample_moisture_sensor.txt | 31 +++++++++++++++++++++++++ wlan.py | 33 +++++++++++++++++++++++++++ 6 files changed, 133 insertions(+), 10 deletions(-) create mode 100644 REDME.md create mode 100644 grow_system.py create mode 100644 moisture_sensor.py create mode 100644 notes/code_sample_moisture_sensor.txt create mode 100644 wlan.py diff --git a/REDME.md b/REDME.md new file mode 100644 index 0000000..bc52edf --- /dev/null +++ b/REDME.md @@ -0,0 +1,3 @@ +# Show output on terminal # +`minicom -b 115200 -o -D /dev/cu.usbmodem3301` + diff --git a/grow_system.py b/grow_system.py new file mode 100644 index 0000000..72f72af --- /dev/null +++ b/grow_system.py @@ -0,0 +1,24 @@ +import time +from moisture_sensor import MoistureSensor + + +class GrowSystem: + + moisture_sensor = None + + most_recent_values = {} + + def __init__(self, settings): + print("hello from GrowSystem") + print(settings) + if not self.moisture_sensor: + self.moisture_sensor = MoistureSensor(settings['moisture_sensor']) + + def start(self): + print("Start reading sensors ...") + while True: + self.moisture_sensor.read() + self.most_recent_values['moisture_sensor'] = self.moisture_sensor.most_recent_value + print(self.most_recent_values) + time.sleep(1) + diff --git a/main.py b/main.py index 94e3a87..90c34d2 100644 --- a/main.py +++ b/main.py @@ -1,16 +1,27 @@ -# This is a sample Python script. +# GrowSystem +# Author: Maik Müller (maik@muelleronlineorg) -# Press ⌃R to execute it or replace it with your code. -# Press Double ⇧ to search everywhere for classes, files, tool windows, actions, and settings. - - -def print_hi(name): - # Use a breakpoint in the code line below to debug your script. - print(f'Hi, {name}') # Press ⌘F8 to toggle the breakpoint. +# WIP! +# This file should do only: +# - provide constants for settings +# - eventually necessary system settings +# - Call base class, permitting the configured constants +from grow_system import GrowSystem +settings = { + 'wlan_ssid': 'Oppa-95.lan', + 'wlan_pw': '95%04-MM', + 'led_pin_int': 15, + 'moisture_sensor': { + 'pin_int': 26 + }, + 'pump_pin_int': 24 +} # Press the green button in the gutter to run the script. if __name__ == '__main__': - print_hi('PyCharm') + print("Start grow system") + gs = GrowSystem(settings) + gs.start() -# See PyCharm help at https://www.jetbrains.com/help/pycharm/ + # See PyCharm help at https://www.jetbrains.com/help/pycharm/ diff --git a/moisture_sensor.py b/moisture_sensor.py new file mode 100644 index 0000000..a7e6deb --- /dev/null +++ b/moisture_sensor.py @@ -0,0 +1,21 @@ +# Moisture Sensor Class +from machine import ADC, Pin + + +class MoistureSensor: + moisture_sensor_pin_int = -1 + + moisture_sensor = None + + most_recent_value = -1 + + def __init__(self, moisture_sensor_data): + self.moisture_sensor_pin_int = moisture_sensor_data['pin_int'] + print("Hello from moisture sensor. Sensor pin is: " + str(self.moisture_sensor_pin_int)) + self.moisture_sensor = ADC(Pin(self.moisture_sensor_pin_int)) + + def read(self): + self.most_recent_value = self.moisture_sensor.read_u16() + + + diff --git a/notes/code_sample_moisture_sensor.txt b/notes/code_sample_moisture_sensor.txt new file mode 100644 index 0000000..64a5eaf --- /dev/null +++ b/notes/code_sample_moisture_sensor.txt @@ -0,0 +1,31 @@ +from machine import Pin, ADC +import time +import network +import urequests +import statistics +import secrets +sensor = ADC(Pin(26)) +wlan = network.WLAN(network.STA_IF) +wlan.active(True) +wlan.connect(secrets.SSID, secrets.PASSWORD) +time.sleep(5) +print(wlan.isconnected()) +readings = [] +try: + while True: + for i in range(5): + reading = sensor.read_u16() + readings.append(reading) + print(readings) + time.sleep(1) + median_value = statistics.median(readings) + if median_value < 400: + urequests.get("https://api.telegram.org/bot"+secrets.API+"/sendMessage?text=Gary is thirsty&chat_id="+secrets.ID) + print("Message Sent") + else: + print("Gary has enough water") + time.sleep(3600) +except OSError: + print("@"*68) + print("@ Cannot connect to the Wi-Fi, please check your SSID and PASSWORD @") +print("@"*68) \ No newline at end of file diff --git a/wlan.py b/wlan.py new file mode 100644 index 0000000..5050ba7 --- /dev/null +++ b/wlan.py @@ -0,0 +1,33 @@ +import machine +import network +import time + +# network.country('DE') + + +class WlanClient: + + ssid = '' + pw = '' + wlan = network.WLAN(network.STA_IF) + # Status-LED + led_onboard = machine.Pin('LED', machine.Pin.OUT) + led_onboard.value(False) + + def __init__(self, ssid, pw): + self.ssid = ssid + self.pw = pw + + def connect(self): + if not self.isConnected(): + print('No WLAN connected. Connecting ...') + self.wlan.active(True) + self.wlan.connect(self.ssid, self.pw) + for i in range(10): + if self.wlan.status() < 0 or self.wlan.status() >= 3: + break + led_value = self.led_onboard.value() == 1 + self.led_onboard.value(led_value) + + def isConnected(self): + return self.wlan.isconnected() \ No newline at end of file From c8f4baf9843aa3e478ab35a850fd877bb3804e71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maik=20Mu=CC=88ller?= Date: Thu, 25 Apr 2024 18:35:50 +0200 Subject: [PATCH 2/9] [WIP] dht22 sensor working so far --- dht22.py | 32 ++++++++++++++++++++++++++++++++ grow_system.py | 11 +++++++++++ main.py | 4 +++- 3 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 dht22.py diff --git a/dht22.py b/dht22.py new file mode 100644 index 0000000..37cc79e --- /dev/null +++ b/dht22.py @@ -0,0 +1,32 @@ +from dht import DHT22 +from machine import ADC, Pin + + +class TemperatureHumiditySensor: + dht22_sensor_pin_int = -1 + + dht22_sensor = None + + most_recent_values = {} + + def __init__(self, settings): + print("Hello from dht22 sensor class") + print(settings) + self.dht22_sensor_pin_int = settings['pin_int'] + self.dht22_sensor = DHT22(Pin(self.dht22_sensor_pin_int, Pin.IN, Pin.PULL_UP)) + + def read(self): + try: + self.dht22_sensor.measure() + self.most_recent_values = { + 'temperature': { + 'value': self.dht22_sensor.temperature(), + 'unit': '°C'}, + 'humidity': { + 'value': self.dht22_sensor.humidity(), + 'unit': '%' + } + } + except OSError: + print('DHT22 Error reading temperature/humidity. Check wires') + print() diff --git a/grow_system.py b/grow_system.py index 72f72af..38b64bb 100644 --- a/grow_system.py +++ b/grow_system.py @@ -1,10 +1,12 @@ import time from moisture_sensor import MoistureSensor +from dht22 import TemperatureHumiditySensor class GrowSystem: moisture_sensor = None + temperature_humidity_sensor = None most_recent_values = {} @@ -14,11 +16,20 @@ class GrowSystem: if not self.moisture_sensor: self.moisture_sensor = MoistureSensor(settings['moisture_sensor']) + if not self.temperature_humidity_sensor: + self.temperature_humidity_sensor = TemperatureHumiditySensor(settings['temperature_humidity_sensor']) + def start(self): print("Start reading sensors ...") while True: + # Moisture Sensor self.moisture_sensor.read() self.most_recent_values['moisture_sensor'] = self.moisture_sensor.most_recent_value + + # Temperature and Humidity Sensor + self.temperature_humidity_sensor.read() + self.most_recent_values['temperature_humidity_sensor'] = self.temperature_humidity_sensor.most_recent_values + print(self.most_recent_values) time.sleep(1) diff --git a/main.py b/main.py index 90c34d2..8b0557d 100644 --- a/main.py +++ b/main.py @@ -11,10 +11,12 @@ from grow_system import GrowSystem settings = { 'wlan_ssid': 'Oppa-95.lan', 'wlan_pw': '95%04-MM', - 'led_pin_int': 15, 'moisture_sensor': { 'pin_int': 26 }, + 'temperature_humidity_sensor': { + 'pin_int': 15 + }, 'pump_pin_int': 24 } From f556189a0b7726360d3278c90798f2e8e820774d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maik=20Mu=CC=88ller?= Date: Fri, 26 Apr 2024 15:54:57 +0200 Subject: [PATCH 3/9] running with two sensors --- device_info.py | 34 ++++++++++++++++++++++++++++++++++ dht22.py | 15 +++++++++------ grow_system.py | 39 +++++++++++++++++++++++++++++++-------- grow_system_api.py | 29 +++++++++++++++++++++++++++++ http_client.py | 35 +++++++++++++++++++++++++++++++++++ main.py | 32 +++++++++++++++++++++++++++++++- moisture_sensor.py | 13 ++++++++----- sensor_data_manager.py | 19 +++++++++++++++++++ wlan.py | 22 ++++++++++++++-------- 9 files changed, 210 insertions(+), 28 deletions(-) create mode 100644 device_info.py create mode 100644 grow_system_api.py create mode 100644 http_client.py create mode 100644 sensor_data_manager.py diff --git a/device_info.py b/device_info.py new file mode 100644 index 0000000..89c0474 --- /dev/null +++ b/device_info.py @@ -0,0 +1,34 @@ +import network + + +class DeviceInfo: + + name = "Dev Device 1" + + token = "PC]-0Bmp83h7F5#U!D6KJ(A&" + + wlan = network.WLAN(network.STA_IF) + + def get_macaddress(self): + return self._format_mac(self.wlan.config('mac').hex()) + + def get_ipaddress(self): + return self.wlan.ifconfig()[0] + + def get_all_device_infos(self): + return { + 'name': self.name, + 'mac_address': self.get_macaddress(), + 'ip_address': self.get_ipaddress(), + 'token': self.token} + + def _format_mac(self, mac): + # Split the MAC address into pairs of two characters each + pairs = [mac[i:i+2] for i in range(0, len(mac), 2)] + # Join the pairs with colons to create the formatted MAC address + formatted_mac = ":".join(pairs) + return formatted_mac + + + + \ No newline at end of file diff --git a/dht22.py b/dht22.py index 37cc79e..9f6a22a 100644 --- a/dht22.py +++ b/dht22.py @@ -7,7 +7,7 @@ class TemperatureHumiditySensor: dht22_sensor = None - most_recent_values = {} + most_recent_values = [] def __init__(self, settings): print("Hello from dht22 sensor class") @@ -18,15 +18,18 @@ class TemperatureHumiditySensor: def read(self): try: self.dht22_sensor.measure() - self.most_recent_values = { - 'temperature': { + self.most_recent_values = [ + { + 'type': 'temperature', 'value': self.dht22_sensor.temperature(), - 'unit': '°C'}, - 'humidity': { + 'unit': 'C' + }, + { + 'type': 'humidity', 'value': self.dht22_sensor.humidity(), 'unit': '%' } - } + ] except OSError: print('DHT22 Error reading temperature/humidity. Check wires') print() diff --git a/grow_system.py b/grow_system.py index 38b64bb..f553d9f 100644 --- a/grow_system.py +++ b/grow_system.py @@ -1,18 +1,24 @@ import time from moisture_sensor import MoistureSensor from dht22 import TemperatureHumiditySensor +from sensor_data_manager import SensorDataManager +from grow_system_api import GrowSystemApi class GrowSystem: + grow_system_api = GrowSystemApi() + moisture_sensor = None temperature_humidity_sensor = None - most_recent_values = {} + most_recent_values = [] + + sensor_data_manager = None + + device_id = None - def __init__(self, settings): - print("hello from GrowSystem") - print(settings) + def __init__(self, settings): if not self.moisture_sensor: self.moisture_sensor = MoistureSensor(settings['moisture_sensor']) @@ -20,16 +26,33 @@ class GrowSystem: self.temperature_humidity_sensor = TemperatureHumiditySensor(settings['temperature_humidity_sensor']) def start(self): + print("Say the server hello...") + result = self.grow_system_api.say_hello() + message = result['message'] + + if message != 'OK': + print("Device not activated. Stopping") + return + + self.device_id = result['data']['device_id'] + self.sensor_data_manager = SensorDataManager(self.device_id) + print("Start reading sensors ...") while True: + # Reset data + self.most_recent_values = [] # Moisture Sensor self.moisture_sensor.read() - self.most_recent_values['moisture_sensor'] = self.moisture_sensor.most_recent_value + self.most_recent_values = self.most_recent_values + self.moisture_sensor.most_recent_value # Temperature and Humidity Sensor self.temperature_humidity_sensor.read() - self.most_recent_values['temperature_humidity_sensor'] = self.temperature_humidity_sensor.most_recent_values - + self.most_recent_values = self.most_recent_values + self.temperature_humidity_sensor.most_recent_values + + print("Most recent bla") print(self.most_recent_values) - time.sleep(1) + + self.sensor_data_manager.handleData(self.most_recent_values) + + time.sleep(5) diff --git a/grow_system_api.py b/grow_system_api.py new file mode 100644 index 0000000..dbd4aff --- /dev/null +++ b/grow_system_api.py @@ -0,0 +1,29 @@ +from http_client import HTTPClient +from device_info import DeviceInfo +import json + + +class GrowSystemApi: + + http_client = HTTPClient() + + device_info = DeviceInfo() + + base_url = 'api.growsystem.muellerdev.kozow.com' + + def say_hello(self): + data = self._get_device_data() + response = self.http_client.post(self.base_url + "/api/device", data) + jsonResult = json.loads(response.text) + print(jsonResult) + return jsonResult; + + def send_measurements(self, device_id, data): + url = self.base_url + "/api/device/" + str(device_id) + "/sensor-log" + print(url) + response = self.http_client.post(url, data) + return json.loads(response.text) + + def _get_device_data(self): + return self.device_info.get_all_device_infos() + diff --git a/http_client.py b/http_client.py new file mode 100644 index 0000000..fd197ed --- /dev/null +++ b/http_client.py @@ -0,0 +1,35 @@ +import urequests +import json + +class HTTPClient: + def __init__(self): + pass + + def get(self, url): + url = 'https://' + url + try: + # headers = {'Content-Type': 'application/json'} + response = urequests.get(url) + if response.status_code == 200: + print("Data sent, got response") + return response + else: + print("Failed to get data. Status code:", response.status_code) + except Exception as e: + print("Exception occurred:", e) + + def post(self, url, data): + url = 'https://' + url + try: + headers = {'Content-Type': 'application/json', 'Accept': 'application/json, text/plain, */*'} + json_data = json.dumps(data) + print("Send post request to: " + url) + response = urequests.post(url, data=json_data, headers=headers) + if response.status_code == 200: + return response + else: + print("Failed to send data. Status code:", response.status_code) + print(response.text) + except Exception as e: + print("Exception occurred:", e) + diff --git a/main.py b/main.py index 8b0557d..3f6e225 100644 --- a/main.py +++ b/main.py @@ -5,8 +5,15 @@ # This file should do only: # - provide constants for settings # - eventually necessary system settings +# - init wlan connection # - Call base class, permitting the configured constants +import network +import urequests from grow_system import GrowSystem +from wlan import WlanClient +from http_client import HTTPClient +from device_info import DeviceInfo + settings = { 'wlan_ssid': 'Oppa-95.lan', @@ -20,10 +27,33 @@ settings = { 'pump_pin_int': 24 } + +def wlan_scan(): + # Client-Betrieb + wlan = network.WLAN(network.STA_IF) + # WLAN-Interface aktivieren + wlan.active(True) + # WLANs ausgeben + found_wlans = wlan.scan() + return found_wlans + + # Press the green button in the gutter to run the script. if __name__ == '__main__': + #print(wlan_scan()) + print("Connect WLAN") + wlanClient = WlanClient(settings['wlan_ssid'], settings['wlan_pw']) + wlanClient.connect() + print("---------------------------------------") + print("") + + di = DeviceInfo() + print("Device Infos:") + print(di.get_all_device_infos()) + print("---------------------------------------") + print("") + print("Start grow system") gs = GrowSystem(settings) gs.start() - # See PyCharm help at https://www.jetbrains.com/help/pycharm/ diff --git a/moisture_sensor.py b/moisture_sensor.py index a7e6deb..851aaea 100644 --- a/moisture_sensor.py +++ b/moisture_sensor.py @@ -7,7 +7,7 @@ class MoistureSensor: moisture_sensor = None - most_recent_value = -1 + most_recent_value = [] def __init__(self, moisture_sensor_data): self.moisture_sensor_pin_int = moisture_sensor_data['pin_int'] @@ -15,7 +15,10 @@ class MoistureSensor: self.moisture_sensor = ADC(Pin(self.moisture_sensor_pin_int)) def read(self): - self.most_recent_value = self.moisture_sensor.read_u16() - - - + self.most_recent_value = [ + { + 'type': 'moisture', + 'value': self.moisture_sensor.read_u16(), + 'unit': 'unknown' + }, + ] diff --git a/sensor_data_manager.py b/sensor_data_manager.py new file mode 100644 index 0000000..f670d93 --- /dev/null +++ b/sensor_data_manager.py @@ -0,0 +1,19 @@ +from grow_system_api import GrowSystemApi + + +class SensorDataManager: + + grow_system_api = None + + base_url = 'api.growsystem.muellerdev.kozow.com' + + device_id = None + + def __init__(self, device_id): + self.grow_system_api = GrowSystemApi() + self.device_id = device_id + + def handleData(self, data): + jsonResponse = self.grow_system_api.send_measurements(self.device_id, data) + print("---- Response: -----") + print(jsonResponse) \ No newline at end of file diff --git a/wlan.py b/wlan.py index 5050ba7..c265d8e 100644 --- a/wlan.py +++ b/wlan.py @@ -1,7 +1,6 @@ import machine import network import time - # network.country('DE') @@ -9,25 +8,32 @@ class WlanClient: ssid = '' pw = '' - wlan = network.WLAN(network.STA_IF) + wlan = None # Status-LED led_onboard = machine.Pin('LED', machine.Pin.OUT) led_onboard.value(False) def __init__(self, ssid, pw): + # print("Hello from wlan class") self.ssid = ssid self.pw = pw + self.wlan = network.WLAN(network.STA_IF) def connect(self): - if not self.isConnected(): - print('No WLAN connected. Connecting ...') + if not self.is_connected(): + print('No WLAN connected. Connecting ...' + self.ssid + ' ' + self.pw) self.wlan.active(True) self.wlan.connect(self.ssid, self.pw) for i in range(10): if self.wlan.status() < 0 or self.wlan.status() >= 3: break - led_value = self.led_onboard.value() == 1 - self.led_onboard.value(led_value) + time.sleep(1) + # led_value = self.led_onboard.value() == 1 + # self.led_onboard.value(led_value) + if self.wlan.isconnected(): + net_config = self.wlan.ifconfig() + print("NetConfig:") + print(net_config) - def isConnected(self): - return self.wlan.isconnected() \ No newline at end of file + def is_connected(self): + return self.wlan.isconnected() From 0de1b04f2efc284ab144942392c727750dd2e740 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maik=20Mu=CC=88ller?= Date: Sat, 27 Apr 2024 05:49:42 +0200 Subject: [PATCH 4/9] improve config --- device_info.py | 5 +++++ grow_system_api.py | 6 +++++- moisture_sensor.py | 20 +++++++++++++++++--- sensor_data_manager.py | 6 ++++-- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/device_info.py b/device_info.py index 89c0474..83c0c1c 100644 --- a/device_info.py +++ b/device_info.py @@ -3,10 +3,15 @@ import network class DeviceInfo: + # Device Infos name = "Dev Device 1" token = "PC]-0Bmp83h7F5#U!D6KJ(A&" + server_url = 'api.growsystem.muelleronline.org' + + # Device Infos End + wlan = network.WLAN(network.STA_IF) def get_macaddress(self): diff --git a/grow_system_api.py b/grow_system_api.py index dbd4aff..51a9b1c 100644 --- a/grow_system_api.py +++ b/grow_system_api.py @@ -9,11 +9,15 @@ class GrowSystemApi: device_info = DeviceInfo() - base_url = 'api.growsystem.muellerdev.kozow.com' + base_url = '' + + def __init__(self): + self.base_url = self.device_info.server_url def say_hello(self): data = self._get_device_data() response = self.http_client.post(self.base_url + "/api/device", data) + print(response.text) jsonResult = json.loads(response.text) print(jsonResult) return jsonResult; diff --git a/moisture_sensor.py b/moisture_sensor.py index 851aaea..c37454b 100644 --- a/moisture_sensor.py +++ b/moisture_sensor.py @@ -8,9 +8,14 @@ class MoistureSensor: moisture_sensor = None most_recent_value = [] + + min_raw_value = None + max_raw_value = None - def __init__(self, moisture_sensor_data): + def __init__(self, moisture_sensor_data, min_raw_value=300, max_raw_value=65535): self.moisture_sensor_pin_int = moisture_sensor_data['pin_int'] + self.min_raw_value = min_raw_value + self.max_raw_value = max_raw_value print("Hello from moisture sensor. Sensor pin is: " + str(self.moisture_sensor_pin_int)) self.moisture_sensor = ADC(Pin(self.moisture_sensor_pin_int)) @@ -18,7 +23,16 @@ class MoistureSensor: self.most_recent_value = [ { 'type': 'moisture', - 'value': self.moisture_sensor.read_u16(), - 'unit': 'unknown' + 'value': self.convert_to_moisture_percentage(self.moisture_sensor.read_u16()), + 'unit': '%' }, ] + + def normalize_sensor_value(self, raw_value): + return (raw_value - self.min_raw_value) / (self.max_raw_value - self.min_raw_value) + + def convert_to_moisture_percentage(self, raw_value): + normalized_value = self.normalize_sensor_value(raw_value) + return round(100 - normalized_value * 100, 1) + + \ No newline at end of file diff --git a/sensor_data_manager.py b/sensor_data_manager.py index f670d93..fc64ff0 100644 --- a/sensor_data_manager.py +++ b/sensor_data_manager.py @@ -1,16 +1,18 @@ from grow_system_api import GrowSystemApi +# from device_info import DeviceInfo class SensorDataManager: - grow_system_api = None + device_info = None - base_url = 'api.growsystem.muellerdev.kozow.com' + grow_system_api = None device_id = None def __init__(self, device_id): self.grow_system_api = GrowSystemApi() + # self.device_info = DeviceInfo() self.device_id = device_id def handleData(self, data): From 2ba5d7816e85ddf84c7f31f2c88ef237e0b4a976 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maik=20Mu=CC=88ller?= Date: Sat, 27 Apr 2024 05:49:59 +0200 Subject: [PATCH 5/9] improve config --- sensors.py | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 sensors.py diff --git a/sensors.py b/sensors.py new file mode 100644 index 0000000..db674ac --- /dev/null +++ b/sensors.py @@ -0,0 +1,3 @@ +class Sensors: + # this is a parent class for the Sensor classes + \ No newline at end of file From a76212ab4d4e8a58daeaef9423a2c0b59403fc53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maik=20Mu=CC=88ller?= Date: Sat, 27 Apr 2024 06:27:34 +0200 Subject: [PATCH 6/9] sensors now added in loop --- device_info.py | 16 +++++++++++++++ dht22.py | 16 +++++++-------- grow_system.py | 49 +++++++++++++++++++++++++++++++++------------- moisture_sensor.py | 18 ++++++++--------- 4 files changed, 68 insertions(+), 31 deletions(-) diff --git a/device_info.py b/device_info.py index 83c0c1c..c0c9a4d 100644 --- a/device_info.py +++ b/device_info.py @@ -10,6 +10,22 @@ class DeviceInfo: server_url = 'api.growsystem.muelleronline.org' + wlan_ssid = 'Oppa-95.lan' + wlan_pw = '95%04-MM' + + sensors = [ + { + 'type': 'moisture', + 'pin_int': 26 + }, + { + 'type': 'dht22', + 'pin_int': 15 + } + ] + +# 'pump_pin_int': 24 + # Device Infos End wlan = network.WLAN(network.STA_IF) diff --git a/dht22.py b/dht22.py index 9f6a22a..05fb9f2 100644 --- a/dht22.py +++ b/dht22.py @@ -3,30 +3,30 @@ from machine import ADC, Pin class TemperatureHumiditySensor: - dht22_sensor_pin_int = -1 + sensor_pin_int = -1 - dht22_sensor = None + sensor = None most_recent_values = [] def __init__(self, settings): - print("Hello from dht22 sensor class") + print("Initialize dht22 sensor. Sensor pin is: " + str(settings['pin_int'])) print(settings) - self.dht22_sensor_pin_int = settings['pin_int'] - self.dht22_sensor = DHT22(Pin(self.dht22_sensor_pin_int, Pin.IN, Pin.PULL_UP)) + self.sensor_pin_int = settings['pin_int'] + self.sensor = DHT22(Pin(self.sensor_pin_int, Pin.IN, Pin.PULL_UP)) def read(self): try: - self.dht22_sensor.measure() + self.sensor.measure() self.most_recent_values = [ { 'type': 'temperature', - 'value': self.dht22_sensor.temperature(), + 'value': self.sensor.temperature(), 'unit': 'C' }, { 'type': 'humidity', - 'value': self.dht22_sensor.humidity(), + 'value': self.sensor.humidity(), 'unit': '%' } ] diff --git a/grow_system.py b/grow_system.py index f553d9f..6e7fd52 100644 --- a/grow_system.py +++ b/grow_system.py @@ -3,27 +3,45 @@ from moisture_sensor import MoistureSensor from dht22 import TemperatureHumiditySensor from sensor_data_manager import SensorDataManager from grow_system_api import GrowSystemApi +from device_info import DeviceInfo class GrowSystem: grow_system_api = GrowSystemApi() - moisture_sensor = None - temperature_humidity_sensor = None + # moisture_sensor = None + # temperature_humidity_sensor = None + + sensors = [] most_recent_values = [] sensor_data_manager = None device_id = None + + device_info = DeviceInfo() def __init__(self, settings): - if not self.moisture_sensor: - self.moisture_sensor = MoistureSensor(settings['moisture_sensor']) + for sensor in self.device_info.sensors: + print("Initialize sensor:") + print(sensor) + sensor_type = sensor['type'] + if sensor_type == 'moisture': + print("Found sensor of type moisture") + self.sensors.append(MoistureSensor(sensor)) + elif sensor_type == 'dht22': + print("Found sensor of type DHT22") + self.sensors.append(TemperatureHumiditySensor(sensor)) + else: + print("No sensor type configured for: " + sensor['type']) + + #if not self.moisture_sensor: + # self.moisture_sensor = MoistureSensor(settings['moisture_sensor']) - if not self.temperature_humidity_sensor: - self.temperature_humidity_sensor = TemperatureHumiditySensor(settings['temperature_humidity_sensor']) + #if not self.temperature_humidity_sensor: + # self.temperature_humidity_sensor = TemperatureHumiditySensor(settings['temperature_humidity_sensor']) def start(self): print("Say the server hello...") @@ -41,17 +59,20 @@ class GrowSystem: while True: # Reset data self.most_recent_values = [] + + for sensor in self.sensors: + sensor.read() + self.most_recent_values = self.most_recent_values + sensor.most_recent_values + print("Most recent bla") + print(sensor.most_recent_values) + # Moisture Sensor - self.moisture_sensor.read() - self.most_recent_values = self.most_recent_values + self.moisture_sensor.most_recent_value + # self.moisture_sensor.read() + # self.most_recent_values = self.most_recent_values + self.moisture_sensor.most_recent_value # Temperature and Humidity Sensor - self.temperature_humidity_sensor.read() - self.most_recent_values = self.most_recent_values + self.temperature_humidity_sensor.most_recent_values - - print("Most recent bla") - print(self.most_recent_values) - + # self.temperature_humidity_sensor.read() + # self.most_recent_values = self.most_recent_values + self.temperature_humidity_sensor.most_recent_values self.sensor_data_manager.handleData(self.most_recent_values) time.sleep(5) diff --git a/moisture_sensor.py b/moisture_sensor.py index c37454b..08f72b7 100644 --- a/moisture_sensor.py +++ b/moisture_sensor.py @@ -3,27 +3,27 @@ from machine import ADC, Pin class MoistureSensor: - moisture_sensor_pin_int = -1 + sensor_pin_int = -1 - moisture_sensor = None + sensor = None - most_recent_value = [] + most_recent_values = [] min_raw_value = None max_raw_value = None - def __init__(self, moisture_sensor_data, min_raw_value=300, max_raw_value=65535): - self.moisture_sensor_pin_int = moisture_sensor_data['pin_int'] + def __init__(self, sensor_data, min_raw_value=300, max_raw_value=65535): + print("Initialize moisture sensor. Sensor pin is: " + str(sensor_data['pin_int'])) + self.sensor_pin_int = sensor_data['pin_int'] self.min_raw_value = min_raw_value self.max_raw_value = max_raw_value - print("Hello from moisture sensor. Sensor pin is: " + str(self.moisture_sensor_pin_int)) - self.moisture_sensor = ADC(Pin(self.moisture_sensor_pin_int)) + self.sensor = ADC(Pin(self.sensor_pin_int)) def read(self): - self.most_recent_value = [ + self.most_recent_values = [ { 'type': 'moisture', - 'value': self.convert_to_moisture_percentage(self.moisture_sensor.read_u16()), + 'value': self.convert_to_moisture_percentage(self.sensor.read_u16()), 'unit': '%' }, ] From b36b1b3c90e6ea357176e65d85ebe8ed61b9f6dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maik=20Mu=CC=88ller?= Date: Sat, 27 Apr 2024 07:30:12 +0200 Subject: [PATCH 7/9] sensors now added in loop --- device_info.py | 5 ++--- dht22.py | 5 +++-- grow_system.py | 15 +++++---------- grow_system_api.py | 6 ++---- main.py | 2 -- moisture_sensor.py | 19 ++++++++++--------- sensor_data_manager.py | 3 +-- sensors.py | 7 +++++++ 8 files changed, 30 insertions(+), 32 deletions(-) diff --git a/device_info.py b/device_info.py index c0c9a4d..77ea2d1 100644 --- a/device_info.py +++ b/device_info.py @@ -23,9 +23,8 @@ class DeviceInfo: 'pin_int': 15 } ] - -# 'pump_pin_int': 24 - + + read_secs = 60 # Device Infos End wlan = network.WLAN(network.STA_IF) diff --git a/dht22.py b/dht22.py index 05fb9f2..c022666 100644 --- a/dht22.py +++ b/dht22.py @@ -1,15 +1,16 @@ +from sensors import Sensors from dht import DHT22 from machine import ADC, Pin -class TemperatureHumiditySensor: - sensor_pin_int = -1 +class TemperatureHumiditySensor(Sensors): sensor = None most_recent_values = [] def __init__(self, settings): + super().__init__(settings) print("Initialize dht22 sensor. Sensor pin is: " + str(settings['pin_int'])) print(settings) self.sensor_pin_int = settings['pin_int'] diff --git a/grow_system.py b/grow_system.py index 6e7fd52..aeaf915 100644 --- a/grow_system.py +++ b/grow_system.py @@ -25,6 +25,7 @@ class GrowSystem: def __init__(self, settings): for sensor in self.device_info.sensors: + print("") print("Initialize sensor:") print(sensor) sensor_type = sensor['type'] @@ -61,19 +62,13 @@ class GrowSystem: self.most_recent_values = [] for sensor in self.sensors: + print("Read sensor of type " + sensor.type + " at pin " + str(sensor.sensor_pin_int)) sensor.read() + for measurement in sensor.most_recent_values: + print(f"Got {measurement['value']} {measurement['unit']} ({measurement['type']})") self.most_recent_values = self.most_recent_values + sensor.most_recent_values - print("Most recent bla") - print(sensor.most_recent_values) - - # Moisture Sensor - # self.moisture_sensor.read() - # self.most_recent_values = self.most_recent_values + self.moisture_sensor.most_recent_value - # Temperature and Humidity Sensor - # self.temperature_humidity_sensor.read() - # self.most_recent_values = self.most_recent_values + self.temperature_humidity_sensor.most_recent_values self.sensor_data_manager.handleData(self.most_recent_values) - time.sleep(5) + time.sleep(self.device_info.read_secs) diff --git a/grow_system_api.py b/grow_system_api.py index 51a9b1c..0e3376c 100644 --- a/grow_system_api.py +++ b/grow_system_api.py @@ -15,16 +15,14 @@ class GrowSystemApi: self.base_url = self.device_info.server_url def say_hello(self): - data = self._get_device_data() - response = self.http_client.post(self.base_url + "/api/device", data) + response = self.http_client.post(self.base_url + "/api/device", self._get_device_data()) print(response.text) - jsonResult = json.loads(response.text) + jsonResult = json.loads(response.text) print(jsonResult) return jsonResult; def send_measurements(self, device_id, data): url = self.base_url + "/api/device/" + str(device_id) + "/sensor-log" - print(url) response = self.http_client.post(url, data) return json.loads(response.text) diff --git a/main.py b/main.py index 3f6e225..241af6c 100644 --- a/main.py +++ b/main.py @@ -44,13 +44,11 @@ if __name__ == '__main__': print("Connect WLAN") wlanClient = WlanClient(settings['wlan_ssid'], settings['wlan_pw']) wlanClient.connect() - print("---------------------------------------") print("") di = DeviceInfo() print("Device Infos:") print(di.get_all_device_infos()) - print("---------------------------------------") print("") print("Start grow system") diff --git a/moisture_sensor.py b/moisture_sensor.py index 08f72b7..e11ca76 100644 --- a/moisture_sensor.py +++ b/moisture_sensor.py @@ -1,10 +1,10 @@ # Moisture Sensor Class +from sensors import Sensors from machine import ADC, Pin -class MoistureSensor: - sensor_pin_int = -1 - +class MoistureSensor(Sensors): + sensor = None most_recent_values = [] @@ -13,6 +13,7 @@ class MoistureSensor: max_raw_value = None def __init__(self, sensor_data, min_raw_value=300, max_raw_value=65535): + super().__init__(sensor_data) print("Initialize moisture sensor. Sensor pin is: " + str(sensor_data['pin_int'])) self.sensor_pin_int = sensor_data['pin_int'] self.min_raw_value = min_raw_value @@ -21,12 +22,12 @@ class MoistureSensor: def read(self): self.most_recent_values = [ - { - 'type': 'moisture', - 'value': self.convert_to_moisture_percentage(self.sensor.read_u16()), - 'unit': '%' - }, - ] + { + 'type': 'moisture', + 'value': self.convert_to_moisture_percentage(self.sensor.read_u16()), + 'unit': '%' + }, + ] def normalize_sensor_value(self, raw_value): return (raw_value - self.min_raw_value) / (self.max_raw_value - self.min_raw_value) diff --git a/sensor_data_manager.py b/sensor_data_manager.py index fc64ff0..87dbf39 100644 --- a/sensor_data_manager.py +++ b/sensor_data_manager.py @@ -17,5 +17,4 @@ class SensorDataManager: def handleData(self, data): jsonResponse = self.grow_system_api.send_measurements(self.device_id, data) - print("---- Response: -----") - print(jsonResponse) \ No newline at end of file + print("Response message: " + jsonResponse['message']) \ No newline at end of file diff --git a/sensors.py b/sensors.py index db674ac..04beaaa 100644 --- a/sensors.py +++ b/sensors.py @@ -1,3 +1,10 @@ class Sensors: # this is a parent class for the Sensor classes + sensor_pin_int = -1 + + type = "unset" + + def __init__(self, settings): + self.type = settings['type'] + \ No newline at end of file From e59712a7f9b0a425146e7bbcf50834b88441c7e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maik=20Mu=CC=88ller?= Date: Sat, 27 Apr 2024 23:36:55 +0200 Subject: [PATCH 8/9] add ambilight sensor class --- device_info.py | 18 +++++++++++++----- grow_system.py | 4 ++++ sensors.py | 3 +++ 3 files changed, 20 insertions(+), 5 deletions(-) diff --git a/device_info.py b/device_info.py index 77ea2d1..65ab51a 100644 --- a/device_info.py +++ b/device_info.py @@ -17,14 +17,22 @@ class DeviceInfo: { 'type': 'moisture', 'pin_int': 26 - }, + }, { - 'type': 'dht22', - 'pin_int': 15 - } + 'type': 'ambilight', + 'pin_int': 8, # for compatibility only + 'pin_int_sda': 8, + 'pin_int_scl': 9 + }, + + + # { + # 'type': 'dht22', + # 'pin_int': 15 + # } ] - read_secs = 60 + read_secs = 5 # Device Infos End wlan = network.WLAN(network.STA_IF) diff --git a/grow_system.py b/grow_system.py index aeaf915..3528ea9 100644 --- a/grow_system.py +++ b/grow_system.py @@ -1,6 +1,7 @@ import time from moisture_sensor import MoistureSensor from dht22 import TemperatureHumiditySensor +from ambilight_sensor import AmbilightSensor from sensor_data_manager import SensorDataManager from grow_system_api import GrowSystemApi from device_info import DeviceInfo @@ -35,6 +36,9 @@ class GrowSystem: elif sensor_type == 'dht22': print("Found sensor of type DHT22") self.sensors.append(TemperatureHumiditySensor(sensor)) + elif sensor_type == 'ambilight': + print("Found sensor of type GY302/BH1750") + self.sensors.append(AmbilightSensor(sensor)) else: print("No sensor type configured for: " + sensor['type']) diff --git a/sensors.py b/sensors.py index 04beaaa..23ad803 100644 --- a/sensors.py +++ b/sensors.py @@ -2,9 +2,12 @@ class Sensors: # this is a parent class for the Sensor classes sensor_pin_int = -1 + sensor = None + type = "unset" def __init__(self, settings): self.type = settings['type'] + print("Initialize " + self.type + " sensor. Sensor pin is: " + str(settings['pin_int'])) \ No newline at end of file From 3942ac787e4f01e4dcd8462a21ddd13483845f8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maik=20Mu=CC=88ller?= Date: Sat, 27 Apr 2024 23:37:01 +0200 Subject: [PATCH 9/9] add ambilight sensor class --- ambilight_sensor.py | 34 +++++++++++ bh1750.old_1/__init__.py | 1 + bh1750.old_1/bh1750.py | 118 +++++++++++++++++++++++++++++++++++++++ lib/bh1750.py | 62 ++++++++++++++++++++ 4 files changed, 215 insertions(+) create mode 100644 ambilight_sensor.py create mode 100644 bh1750.old_1/__init__.py create mode 100644 bh1750.old_1/bh1750.py create mode 100644 lib/bh1750.py diff --git a/ambilight_sensor.py b/ambilight_sensor.py new file mode 100644 index 0000000..c8f0a3c --- /dev/null +++ b/ambilight_sensor.py @@ -0,0 +1,34 @@ +from sensors import Sensors +from machine import Pin, I2C +from utime import sleep +from lib.bh1750 import BH1750 + + +class AmbilightSensor(Sensors): + + + most_recent_values = [] + + def __init__(self, settings): + super().__init__(settings) + print(settings) # TODO remove + self.sensor_pin_int = settings['pin_int'] + # self.sensor = DHT22(Pin(self.sensor_pin_int, Pin.IN, Pin.PULL_UP)) + self.sensor = BH1750(I2C(0, sda=Pin(settings['pin_int_sda']), scl=Pin(settings['pin_int_scl']))) + + def read(self): + try: + measurement = self.sensor.luminance(BH1750.ONCE_HIRES_1) + print("ambilight ..") + print(measurement) + self.most_recent_values = [ + { + 'type': 'ambilight', + 'value': measurement, + 'unit': '-' + }, + ] + except OSError: + print('Ambilight Error reading temperature/humidity. Check wires') + print() + diff --git a/bh1750.old_1/__init__.py b/bh1750.old_1/__init__.py new file mode 100644 index 0000000..ab5e217 --- /dev/null +++ b/bh1750.old_1/__init__.py @@ -0,0 +1 @@ +from .bh1750 import BH1750 diff --git a/bh1750.old_1/bh1750.py b/bh1750.old_1/bh1750.py new file mode 100644 index 0000000..21cebbe --- /dev/null +++ b/bh1750.old_1/bh1750.py @@ -0,0 +1,118 @@ +# https://github.com/flrrth/pico-bh1750 + +import math + +from micropython import const +from utime import sleep_ms + + +class BH1750: + """Class for the BH1750 digital Ambient Light Sensor + + The datasheet can be found at https://components101.com/sites/default/files/component_datasheet/BH1750.pdf + """ + + MEASUREMENT_MODE_CONTINUOUSLY = const(1) + MEASUREMENT_MODE_ONE_TIME = const(2) + + RESOLUTION_HIGH = const(0) + RESOLUTION_HIGH_2 = const(1) + RESOLUTION_LOW = const(2) + + MEASUREMENT_TIME_DEFAULT = const(69) + MEASUREMENT_TIME_MIN = const(31) + MEASUREMENT_TIME_MAX = const(254) + + def __init__(self, address, i2c): + self._address = address + self._i2c = i2c + self._measurement_mode = BH1750.MEASUREMENT_MODE_ONE_TIME + self._resolution = BH1750.RESOLUTION_HIGH + self._measurement_time = BH1750.MEASUREMENT_TIME_DEFAULT + + self._write_measurement_time() + self._write_measurement_mode() + + def configure(self, measurement_mode: int, resolution: int, measurement_time: int): + """Configures the BH1750. + + Keyword arguments: + measurement_mode -- measure either continuously or once + resolution -- return measurements in either high, high2 or low resolution + measurement_time -- the duration of a single measurement + """ + if measurement_time not in range(BH1750.MEASUREMENT_TIME_MIN, BH1750.MEASUREMENT_TIME_MAX + 1): + raise ValueError("measurement_time must be between {0} and {1}" + .format(BH1750.MEASUREMENT_TIME_MIN, BH1750.MEASUREMENT_TIME_MAX)) + + self._measurement_mode = measurement_mode + self._resolution = resolution + self._measurement_time = measurement_time + + self._write_measurement_time() + self._write_measurement_mode() + + def _write_measurement_time(self): + buffer = bytearray(1) + + high_bit = 1 << 6 | self._measurement_time >> 5 + low_bit = 3 << 5 | (self._measurement_time << 3) >> 3 + + buffer[0] = high_bit + self._i2c.writeto(self._address, buffer) + + buffer[0] = low_bit + self._i2c.writeto(self._address, buffer) + + def _write_measurement_mode(self): + buffer = bytearray(1) + + buffer[0] = self._measurement_mode << 4 | self._resolution + self._i2c.writeto(self._address, buffer) + sleep_ms(24 if self._measurement_time == BH1750.RESOLUTION_LOW else 180) + + def reset(self): + """Clear the illuminance data register.""" + self._i2c.writeto(self._address, bytearray(b'\x07')) + + def power_on(self): + """Powers on the BH1750.""" + self._i2c.writeto(self._address, bytearray(b'\x01')) + + def power_off(self): + """Powers off the BH1750.""" + self._i2c.writeto(self._address, bytearray(b'\x00')) + + @property + def measurement(self) -> float: + """Returns the latest measurement.""" + if self._measurement_mode == BH1750.MEASUREMENT_MODE_ONE_TIME: + self._write_measurement_mode() + + buffer = bytearray(2) + self._i2c.readfrom_into(self._address, buffer) + lux = (buffer[0] << 8 | buffer[1]) / (1.2 * (BH1750.MEASUREMENT_TIME_DEFAULT / self._measurement_time)) + + if self._resolution == BH1750.RESOLUTION_HIGH_2: + return lux / 2 + else: + return lux + + def measurements(self) -> float: + """This is a generator function that continues to provide the latest measurement. Because the measurement time + is greatly affected by resolution and the configured measurement time, this function attemts to calculate the + appropriate sleep time between measurements. + + Example usage: + + for measurement in bh1750.measurements(): # bh1750 is an instance of this class + print(measurement) + """ + while True: + yield self.measurement + + if self._measurement_mode == BH1750.MEASUREMENT_MODE_CONTINUOUSLY: + base_measurement_time = 16 if self._measurement_time == BH1750.RESOLUTION_LOW else 120 + sleep_ms(math.ceil(base_measurement_time * self._measurement_time / BH1750.MEASUREMENT_TIME_DEFAULT)) + + diff --git a/lib/bh1750.py b/lib/bh1750.py new file mode 100644 index 0000000..0f91b49 --- /dev/null +++ b/lib/bh1750.py @@ -0,0 +1,62 @@ +""" +Micropython BH1750 ambient light sensor driver. +* https://github.com/PinkInk/upylib/tree/master/bh1750 +""" + +from utime import sleep_ms + + +class BH1750(): + """Micropython BH1750 ambient light sensor driver.""" + + PWR_OFF = 0x00 + PWR_ON = 0x01 + RESET = 0x07 + + # modes + CONT_LOWRES = 0x13 + CONT_HIRES_1 = 0x10 + CONT_HIRES_2 = 0x11 + ONCE_HIRES_1 = 0x20 + ONCE_HIRES_2 = 0x21 + ONCE_LOWRES = 0x23 + + # default addr=0x23 if addr pin floating or pulled to ground + # addr=0x5c if addr pin pulled high + def __init__(self, bus, addr=0x23): + self.bus = bus + self.addr = addr + self.off() + self.reset() + + def off(self): + """Turn sensor off.""" + self.set_mode(self.PWR_OFF) + + def on(self): + """Turn sensor on.""" + self.set_mode(self.PWR_ON) + + def reset(self): + """Reset sensor, turn on first if required.""" + self.on() + self.set_mode(self.RESET) + + def set_mode(self, mode): + """Set sensor mode.""" + self.mode = mode + self.bus.writeto(self.addr, bytes([self.mode])) + + def luminance(self, mode): + """Sample luminance (in lux), using specified sensor mode.""" + # continuous modes + if mode & 0x10 and mode != self.mode: + self.set_mode(mode) + # one shot modes + if mode & 0x20: + self.set_mode(mode) + # earlier measurements return previous reading + sleep_ms(24 if mode in (0x13, 0x23) else 180) + data = self.bus.readfrom(self.addr, 2) + factor = 2.0 if mode in (0x11, 0x21) else 1.0 + return (data[0]<<8 | data[1]) / (1.2 * factor) \ No newline at end of file