diff --git a/README.md b/README.md deleted file mode 100644 index f90de4d..0000000 --- a/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# GrowSystem - diff --git a/REDME.md b/REDME.md deleted file mode 100644 index bc52edf..0000000 --- a/REDME.md +++ /dev/null @@ -1,3 +0,0 @@ -# Show output on terminal # -`minicom -b 115200 -o -D /dev/cu.usbmodem3301` - diff --git a/bh1750.old_1/__init__.py b/bh1750.old_1/__init__.py deleted file mode 100644 index ab5e217..0000000 --- a/bh1750.old_1/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .bh1750 import BH1750 diff --git a/bh1750.old_1/bh1750.py b/bh1750.old_1/bh1750.py deleted file mode 100644 index 21cebbe..0000000 --- a/bh1750.old_1/bh1750.py +++ /dev/null @@ -1,118 +0,0 @@ -# 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/device_info.py b/device_info.py deleted file mode 100644 index 65ab51a..0000000 --- a/device_info.py +++ /dev/null @@ -1,62 +0,0 @@ -import network - - -class DeviceInfo: - - # Device Infos - name = "Dev Device 1" - - token = "PC]-0Bmp83h7F5#U!D6KJ(A&" - - server_url = 'api.growsystem.muelleronline.org' - - wlan_ssid = 'Oppa-95.lan' - wlan_pw = '95%04-MM' - - sensors = [ - { - 'type': 'moisture', - 'pin_int': 26 - }, - { - 'type': 'ambilight', - 'pin_int': 8, # for compatibility only - 'pin_int_sda': 8, - 'pin_int_scl': 9 - }, - - - # { - # 'type': 'dht22', - # 'pin_int': 15 - # } - ] - - read_secs = 5 - # Device Infos End - - 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/grow_system.py b/grow_system.py deleted file mode 100644 index 3528ea9..0000000 --- a/grow_system.py +++ /dev/null @@ -1,78 +0,0 @@ -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 - - -class GrowSystem: - - grow_system_api = GrowSystemApi() - - # 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): - for sensor in self.device_info.sensors: - print("") - 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)) - 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']) - - #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("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 = [] - - 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 - - self.sensor_data_manager.handleData(self.most_recent_values) - - time.sleep(self.device_info.read_secs) - diff --git a/grow_system_api.py b/grow_system_api.py deleted file mode 100644 index 0e3376c..0000000 --- a/grow_system_api.py +++ /dev/null @@ -1,31 +0,0 @@ -from http_client import HTTPClient -from device_info import DeviceInfo -import json - - -class GrowSystemApi: - - http_client = HTTPClient() - - device_info = DeviceInfo() - - base_url = '' - - def __init__(self): - self.base_url = self.device_info.server_url - - def say_hello(self): - response = self.http_client.post(self.base_url + "/api/device", self._get_device_data()) - print(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" - 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/gs/classes/a_sensors.py b/gs/classes/a_sensors.py new file mode 100644 index 0000000..af4244e --- /dev/null +++ b/gs/classes/a_sensors.py @@ -0,0 +1,13 @@ +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'])) + + diff --git a/gs/classes/http_request.py b/gs/classes/http_request.py new file mode 100644 index 0000000..c85063c --- /dev/null +++ b/gs/classes/http_request.py @@ -0,0 +1,72 @@ +import json +import ure as re + + +class HttpRequest: + + METHOD = { + "GET": "GET", + "POST": "POST" + } + + method = '' + path = '' + raw_content = '' + headers = {} + + def __init__(self, request): + self.original_request = request + # self.method = method + # self.path = path + # self.headers = headers + # self.raw_content = content + print("http request initialized") + self.parse_request(request) + + def parse_request(self, request): + # Split the request into lines + lines = request.decode().split('\r\n') + + # Extract method, path, and HTTP version from the first line + self.method, self.path, _ = lines[0].split() + + # Parse headers + for line in lines[1:]: + if not line: + break # Empty line indicates end of headers + key, value = line.split(': ', 1) + self.headers[key] = value + + # Content is assumed to be in the last line + self.raw_content = lines[-1] + + def get_content_json(self): + # Parse the POST request content into a dictionary + parsed_content = {} + pairs = self.raw_content.split('&') + for pair in pairs: + key, value = pair.split('=') + parsed_content[key] = value + + # Encode the values in the dictionary + encoded_content = {} + for key, value in parsed_content.items(): + encoded_value = re.sub(r'%([0-9A-Fa-f]{2})', lambda m: chr(int(m.group(1), 16)), value) + encoded_content[key] = encoded_value + + return encoded_content + + def __str__(self): + return { + "method": self.method, + "headers": self.headers, + "content": self.raw_content, + "content_json": self.get_content_json() + } + + + + + + + diff --git a/sensors.py b/gs/classes/sensors.py similarity index 98% rename from sensors.py rename to gs/classes/sensors.py index 23ad803..af4244e 100644 --- a/sensors.py +++ b/gs/classes/sensors.py @@ -10,4 +10,4 @@ class Sensors: self.type = settings['type'] print("Initialize " + self.type + " sensor. Sensor pin is: " + str(settings['pin_int'])) - \ No newline at end of file + diff --git a/ambilight_sensor.py b/gs/classes/sensors/ambilight_sensor.py similarity index 95% rename from ambilight_sensor.py rename to gs/classes/sensors/ambilight_sensor.py index c8f0a3c..df1d966 100644 --- a/ambilight_sensor.py +++ b/gs/classes/sensors/ambilight_sensor.py @@ -1,4 +1,4 @@ -from sensors import Sensors +from gs.classes.a_sensors import Sensors from machine import Pin, I2C from utime import sleep from lib.bh1750 import BH1750 @@ -32,3 +32,4 @@ class AmbilightSensor(Sensors): print('Ambilight Error reading temperature/humidity. Check wires') print() + diff --git a/dht22.py b/gs/classes/sensors/dht22.py similarity index 96% rename from dht22.py rename to gs/classes/sensors/dht22.py index c022666..464b31d 100644 --- a/dht22.py +++ b/gs/classes/sensors/dht22.py @@ -1,4 +1,4 @@ -from sensors import Sensors +from gs.classes.a_sensors import Sensors from dht import DHT22 from machine import ADC, Pin @@ -34,3 +34,4 @@ class TemperatureHumiditySensor(Sensors): except OSError: print('DHT22 Error reading temperature/humidity. Check wires') print() + diff --git a/moisture_sensor.py b/gs/classes/sensors/moisture_sensor.py similarity index 96% rename from moisture_sensor.py rename to gs/classes/sensors/moisture_sensor.py index e11ca76..145cf9a 100644 --- a/moisture_sensor.py +++ b/gs/classes/sensors/moisture_sensor.py @@ -1,5 +1,5 @@ # Moisture Sensor Class -from sensors import Sensors +from gs.classes.a_sensors import Sensors from machine import ADC, Pin @@ -36,4 +36,4 @@ class MoistureSensor(Sensors): normalized_value = self.normalize_sensor_value(raw_value) return round(100 - normalized_value * 100, 1) - \ No newline at end of file + diff --git a/gs/config/app.py b/gs/config/app.py new file mode 100644 index 0000000..f066dbd --- /dev/null +++ b/gs/config/app.py @@ -0,0 +1,2 @@ +api_url = 'api.growsystem.muellerdev.kozow.com' +read_secs = 1 * 60 diff --git a/gs/config/examples/device_config.json b/gs/config/examples/device_config.json new file mode 100644 index 0000000..901d060 --- /dev/null +++ b/gs/config/examples/device_config.json @@ -0,0 +1 @@ +{"sensors": [{"pin_int": 15, "type": "dht22"}, {"pin_int": 26, "type": "moisture"}], "device_id": 9, "name": "Fulltest1", "token": "uStIrOgScrpbUr0Y", "user_id": 1} \ No newline at end of file diff --git a/gs/config/examples/initial_config.py b/gs/config/examples/initial_config.py new file mode 100644 index 0000000..9cd4fe6 --- /dev/null +++ b/gs/config/examples/initial_config.py @@ -0,0 +1 @@ +config = {"ssid": "Oppa-95.lan", "user_id": "1", "password": "95%04-MM", "pin": 9534} \ No newline at end of file diff --git a/gs/device_info.py b/gs/device_info.py new file mode 100644 index 0000000..93f89aa --- /dev/null +++ b/gs/device_info.py @@ -0,0 +1,66 @@ +import network +import json + + +class DeviceInfo: + + wlan = network.WLAN(network.STA_IF) + + app_version = "1.0.0" + + 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.get_name(), + 'mac_address': self.get_macaddress(), + 'ip_address': self.get_ipaddress(), + 'token': self.get_token()} + + def config(self, filepath=None): + return self._loadJsonConfig(filepath) + + def app_config(self): + import gs.config.app as app_config + return app_config + + def server_url(self): + return self.app_config().api_url + + def get_token(self): + return self.config()['token'] if self.config() and self.config()['token'] else '' + + def get_name(self): + return self.config()['name'] if self.config() and self.config()['name'] else '' + + def get_device_id(self): + return self.config()['device_id'] if self.config() and self.config()['device_id'] else '' + + 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 + + def _loadJsonConfig(self, filepath=None): + filepath = filepath if filepath else '/gs/config/device_config.json' + try: + file = open(filepath, "r") + json_content = file.read() + return json.loads(json_content) + except OSError: # open failed + print("File not found: ", filepath) + return None + + #with open(filepath, 'r') as file: + # json_content = file.read() + #return json.loads(json_content) + + + + \ No newline at end of file diff --git a/gs/exceptions/not_subscriptable_error.py b/gs/exceptions/not_subscriptable_error.py new file mode 100644 index 0000000..19fa115 --- /dev/null +++ b/gs/exceptions/not_subscriptable_error.py @@ -0,0 +1,2 @@ +class NotSubscriptableError(Exception): + pass diff --git a/gs/grow_system_api.py b/gs/grow_system_api.py new file mode 100644 index 0000000..2c85799 --- /dev/null +++ b/gs/grow_system_api.py @@ -0,0 +1,64 @@ +from gs.http_client import HTTPClient +from gs.device_info import DeviceInfo +from gs.wlan_client import WlanClient +import json +import gs.config.initial_config as ic + + +class GrowSystemApi: + + http_client = HTTPClient() + + device_info = DeviceInfo() + + base_url = '' + + def __init__(self): + self.base_url = self.device_info.server_url() + self.connect_wifi(ic.config['ssid'], ic.config['password']) + + # config = self.device_info.config() + # print("Config:", config) + # print("Test", config['test']) + + def activate(self, config): + print("ACtivate config:", config) + data = self._get_device_data() + data.update({ + 'user_id': 1, + 'pin': config['pin'] + }) + print("activate ...", data) + response = self.http_client.post(self.base_url + "/api/device/activate", data) + print("REsponse ...", response) + return self._get_json_encoded(response.text) + + def say_hello(self): + response = self.http_client.post(self.base_url + "/api/device", self._get_device_data()) + print(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" + response = self.http_client.post(url, data) + try: + return json.loads(response.text) + except ValueError as e: + print("JSON Value error raised after sending measurements") + except Exception as e: + print("Exception raised while sending measurements", e) + return response + + def _get_device_data(self): + return self.device_info.get_all_device_infos() + + def _get_json_encoded(self, text): + return json.loads(text) + + def connect_wifi(self, ssid, password): + print("Connect WLAN") + self.wlan_client = WlanClient(ssid, password) + self.wlan_client.connect() + diff --git a/gs/growsystem.py b/gs/growsystem.py new file mode 100644 index 0000000..6046be1 --- /dev/null +++ b/gs/growsystem.py @@ -0,0 +1,129 @@ +from gs.setup import Setup +from gs.device_info import DeviceInfo +from gs.sensor_data_manager import SensorDataManager +import os +import ujson +import machine +import time +from gs.classes.sensors.ambilight_sensor import AmbilightSensor +from gs.classes.sensors.dht22 import TemperatureHumiditySensor +from gs.classes.sensors.moisture_sensor import MoistureSensor + + +class GrowSystem: + + version = "1.0" + + initial_config = None + + device_info = DeviceInfo() + + sensors = [] + + def __init__(self): + print("Initialize Growsystem", self.version) + + if not self._is_initial_config_existing(): + print("No config existing. Start setup ...") + self._setup() + return + + from gs.grow_system_api import GrowSystemApi as GSA + self.gsapi = GSA() + + if self._is_config_existing(): + print("Skip Setup. Config existing.") + self._initialize_sensors() + elif self._is_initial_config_existing(): + print("Initial config only existing (no base config). Start activation ...") + self._activate() + + def start(self): + self.sensor_data_manager = SensorDataManager(self.device_info.get_device_id()) + + print("Start reading sensors ...") + + read_secs = self.device_info.app_config().read_secs + + while True: + # Reset data + 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 + + self.sensor_data_manager.handleData(self.most_recent_values) + + time.sleep(read_secs) + + def _setup(self): + setup = Setup() + setup.setup_pico() + machine.reset + + def _activate(self): + print("Start activation!") + import gs.config.initial_config as ic + self.initial_config = ic.config + device_config = self.gsapi.activate(self.initial_config) + #print("Device Config:", device_config['data']) + + if True: + sensors = device_config['data']['sensors'] + sensor_configs = [] + for sensor in sensors: + sensor_configs.append(sensor['config']) + print(sensor['config']) + device_configs = { + 'name': device_config['data']['name'], + 'device_id': device_config['data']['id'], + 'token': device_config['data']['token'], + 'user_id': device_config['data']['user_id'], + 'sensors': sensor_configs + } + with open("/gs/config/device_config.json", "w") as f: + f.write(ujson.dumps(device_configs)) + if self._is_config_existing(): + machine.reset + + def _is_config_existing(self): + return self._is_file_existing('/gs/config/device_config.json') + + def _is_initial_config_existing(self): + return self._is_file_existing('/gs/config/initial_config.py') + + def _is_file_existing(self, filepath): + try: + f = open(filepath, "r") + f.close() + # continue with the file. + return True + except OSError: # open failed + # handle the file open cas + return False + + def _initialize_sensors(self): + # Init sensors + sensors = self.device_info.config()['sensors'] + for sensor in sensors: + print("--------------------------------------") + 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)) + 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/gs/http_client.py b/gs/http_client.py new file mode 100644 index 0000000..b819ff8 --- /dev/null +++ b/gs/http_client.py @@ -0,0 +1,36 @@ +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: + print("Request OK (200)") + else: + print("Failed to send data.", response.status_code, response.text) + return response + except Exception as e: + print("Exception raised:", e) + return None + diff --git a/gs/little_apache.py b/gs/little_apache.py new file mode 100644 index 0000000..774a383 --- /dev/null +++ b/gs/little_apache.py @@ -0,0 +1,111 @@ +import socket +from gs.classes.http_request import HttpRequest + + +class LittleApache(): + + available_wifis = [] + keep_webserver_alive = True + + + def __init__(self, net): + self.net = net + addr = socket.getaddrinfo('0.0.0.0', 80)[0][-1] + self.s = socket.socket() + self.s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + self.s.bind(addr) + self.s.listen() + # print('Listening on', addr) + + def start(self): + print("Webserver started. Connect to: " + self.net.ifconfig()[0]) + print(self.net.ifconfig()) + + while self.keep_webserver_alive: + try: + conn, addr = self.s.accept() + #print('Got a connection from', addr) + + # Receive and parse the request + request = conn.recv(1024) + # print("Request (RAW)", request) + http_request = HttpRequest(request) + self.http_request = http_request + request = str(request) + # print('Request content = %s' % request) + #try: + # request = request.split()[1] + # print('Request:', request) + #except IndexError: + # pass + response = self.response(http_request) + # Send the HTTP response and close the connection + conn.send('HTTP/1.0 200 OK\r\nContent-type: text/html\r\n\r\n') + conn.send(response) + conn.close() + except OSError as e: + conn.close() + print('Connection closed') + return self + + def response(self, request: HttpRequest): + #print("Webpage: ", request) + print("Request method: ", request.method, "Request path: ", request.path) + + header = f""" + + + + Growsystem + + + + """ + + footer = f""" + + + """ + + body = "" + + if self.is_path_match(request, '/test1'): + body = f""" +
Test 1!!!
+ """ + elif (self.is_path_match(request, '') or self.is_path_match(request, '/')): + options = [] + for w in self.available_wifis: + options.append(''.format(w, w)) + body = """ +

Setup Pico

+
+ SSID:
+ Password:

+ ID:
+ PIN:
+ +
+ """ + body = body.format(''.join(options)) + elif self.is_path_match(request, '/save', 'POST'): + print("Save config path: ", request) + self.keep_webserver_alive = False + body = """ +

Setup Pico

+
Setup abgeschlossen. Bitte ein paar Sekunden warten, dann neu starten.
+ """ + else: + body = f""" +
Unknown page
+ """ + + html = '' + html_arr = [header, body, footer] + html = html.join(html_arr) + return str(html) + + def is_path_match(self, request, path, method='GET'): + return path == request.path and method == request.method + + diff --git a/gs/sensor_data_manager.py b/gs/sensor_data_manager.py new file mode 100644 index 0000000..806870e --- /dev/null +++ b/gs/sensor_data_manager.py @@ -0,0 +1,24 @@ +from gs.grow_system_api import GrowSystemApi +# from device_info import DeviceInfo +from gs.exceptions.not_subscriptable_error import NotSubscriptableError + +class SensorDataManager: + + device_info = None + + 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): + json_response = self.grow_system_api.send_measurements(self.device_id, data) + try: + print("Response message: " + json_response['message']) + except TypeError as e: + print("The response is no json data", e, json_response) + diff --git a/gs/setup.py b/gs/setup.py new file mode 100644 index 0000000..fd5fea9 --- /dev/null +++ b/gs/setup.py @@ -0,0 +1,85 @@ +import network +import ujson +import ure as re +import usocket as socket +import time +from gs.little_apache import LittleApache + + +class Setup: + + wlans = [] + + def __init__(self): + self.ap_ssid = "Growsystem 1.0" + self.ap_password = "password" + self.wlans = [] + self.selected_ssid = "" + self.wlan_password = "" + self.pin = "" + + def scan_wlans(self): + print("Scan for WiFis") + wlan = network.WLAN(network.STA_IF) + wlan.active(True) + self.wlans = [w[0].decode() for w in wlan.scan()] + print("Detected WiFis: ", self.wlans) + wlan.active(False) + + def start_ap_mode(self): + wlan = network.WLAN(network.STA_IF) + wlan.active(False) + wlan_config = { + "ssid": self.ap_ssid, + "pw": self.ap_password + } + print("Switch to ap mode with data:", wlan_config) + ap = network.WLAN(network.AP_IF) + ap.config(essid=self.ap_ssid, password=self.ap_password) + ap.active(True) + self.net = ap + print("Connect with your browser to:", ap.ifconfig()[0]) + + def stop_ap_mode(self): + print("Stop ap mode") + ap = network.WLAN(network.AP_IF) + ap.active(False) + + def get_initial_config_webserver(self): + self.la = LittleApache(self.net) + self.la.available_wifis = self.wlans + self.la.start() + config = self.la.http_request.get_content_json() + print("start webserver end:", config) + return config + + def save_config(self, config): + config = { + "ssid": config['ssid'], + "password": config['password'], + "pin": config['pin'], + "user_id": config['user_id'] + } + print("Save initial config:", config) + with open("/gs/config/initial_config.py", "w") as f: + f.write("config = " + ujson.dumps(config)) + + def switch_to_client_mode(self): + print("Switch to client mode") + ap = network.WLAN(network.AP_IF) + ap.active(False) + wlan = network.WLAN(network.STA_IF) + wlan.active(True) + wlan.connect(self.selected_ssid, self.wlan_password) + while not wlan.isconnected(): + time.sleep(1) + print("Connected to", self.selected_ssid) + + def setup_pico(self): + self.scan_wlans() + self.start_ap_mode() + config = self.get_initial_config_webserver() + self.save_config(config) + # self.stop_ap_mode() + # self.switch_to_client_mode() + diff --git a/wlan.py b/gs/wlan_client.py similarity index 99% rename from wlan.py rename to gs/wlan_client.py index c265d8e..723832f 100644 --- a/wlan.py +++ b/gs/wlan_client.py @@ -37,3 +37,4 @@ class WlanClient: def is_connected(self): return self.wlan.isconnected() + diff --git a/lib/bh1750.py b/lib/bh1750.py index 0f91b49..e155a09 100644 --- a/lib/bh1750.py +++ b/lib/bh1750.py @@ -59,4 +59,4 @@ class BH1750(): 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 + return (data[0]<<8 | data[1]) / (1.2 * factor) diff --git a/main.py b/main.py index 241af6c..ae2ca15 100644 --- a/main.py +++ b/main.py @@ -1,57 +1,13 @@ -# GrowSystem -# Author: Maik Müller (maik@muelleronlineorg) - -# WIP! -# 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 +## Only start grow system +from gs.growsystem import GrowSystem +from time import sleep -settings = { - 'wlan_ssid': 'Oppa-95.lan', - 'wlan_pw': '95%04-MM', - 'moisture_sensor': { - 'pin_int': 26 - }, - 'temperature_humidity_sensor': { - 'pin_int': 15 - }, - '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("") - - di = DeviceInfo() - print("Device Infos:") - print(di.get_all_device_infos()) - print("") - - print("Start grow system") - gs = GrowSystem(settings) + gs = GrowSystem() gs.start() - + + while True: + print("Keep running in main.py") + sleep(5) + \ No newline at end of file diff --git a/notes/code_sample_moisture_sensor.txt b/notes/code_sample_moisture_sensor.txt deleted file mode 100644 index 64a5eaf..0000000 --- a/notes/code_sample_moisture_sensor.txt +++ /dev/null @@ -1,31 +0,0 @@ -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/sensor_data_manager.py b/sensor_data_manager.py deleted file mode 100644 index 87dbf39..0000000 --- a/sensor_data_manager.py +++ /dev/null @@ -1,20 +0,0 @@ -from grow_system_api import GrowSystemApi -# from device_info import DeviceInfo - - -class SensorDataManager: - - device_info = None - - 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): - jsonResponse = self.grow_system_api.send_measurements(self.device_id, data) - print("Response message: " + jsonResponse['message']) \ No newline at end of file