diff --git a/src/configuration/devices.json b/src/configuration/devices.json index c95e522..31f7597 100644 --- a/src/configuration/devices.json +++ b/src/configuration/devices.json @@ -12,7 +12,7 @@ "data": { "loopSpanMs": 500, - "pin": 1, + "pin": 16, "temperature": { "unit": "Celsius", @@ -22,7 +22,7 @@ "threshold":{ "type": "value", "value": 0.2, - "minimalIntervalSeconds": 20 + "minimalIntervalSeconds": 4 } }, "humidity": @@ -33,12 +33,11 @@ "threshold":{ "type": "value", "value": 1, - "minimalIntervalSeconds": 20 + "minimalIntervalSeconds": 4 } } } }, - { "id": "2", "type": "soil", @@ -61,15 +60,42 @@ "sendAsJson": true, "threshold":{ "type": "value", - "value": 1, - "minimalIntervalSeconds": 20 + "value": 5, + "minimalIntervalSeconds": 5 } } } }, - { "id": "3", + "type": "soil", + "name": "gravity_v1", + "ground": 38, + "vcc": 36, + "availability": { + "enabled": true, + "topic": "availability" + }, + "data": + { + "loopSpanMs": 500, + "mux_id": 1, + "channel": 15, + "moisture": + { + "unit": "Percentage", + "topic": "moisture", + "sendAsJson": true, + "threshold":{ + "type": "value", + "value": 1, + "minimalIntervalSeconds": 5 + } + } + } + }, + { + "id": "4", "type": "light", "name": "is", "ground": 38, @@ -81,8 +107,7 @@ "data": { "loopSpanMs": 500, - "mux_id": 1, - "channel": 2, + "adcPin": 27, "insolation": { "unit": "Percentage", @@ -90,10 +115,11 @@ "sendAsJson": true, "threshold":{ "type": "value", - "value": 1, - "minimalIntervalSeconds": 20 + "value": 4, + "minimalIntervalSeconds": 5 } } } } ] + diff --git a/src/drivers/insolation.py b/src/drivers/insolation.py deleted file mode 100644 index 1af6c2a..0000000 --- a/src/drivers/insolation.py +++ /dev/null @@ -1,22 +0,0 @@ -from features.analog_accessor.analog_accessor import AnalogAccessor - - -class InsolationDriver: - def __init__(self, analog_accessor: AnalogAccessor, channel, min_insolation=0, max_insolation=65535): - self.analog_accessor = analog_accessor - self.insolation_val = -1 - self.channel = channel - self.min_insolation = min_insolation - self.max_insolation = max_insolation - - async def measure(self): - val = await self.analog_accessor.read(self.channel) - self.insolation_val = val - - - def insolation(self): - if self.insolation_val < 0: - raise RuntimeError("insolation() method called before measure()", self.insolation_val) - return int((self.insolation_val - self.min_insolation) * 100 / (self.max_insolation - self.min_insolation)) - - \ No newline at end of file diff --git a/src/drivers/sms.py b/src/drivers/sms.py deleted file mode 100644 index 4bd826c..0000000 --- a/src/drivers/sms.py +++ /dev/null @@ -1,20 +0,0 @@ -from features.analog_accessor.analog_accessor import AnalogAccessor - -class SMSDriver: - def __init__(self, analog_accessor: AnalogAccessor, channel, min_moist=0, max_moist=65535): - self.analog_accessor = analog_accessor - self.moisture_val = -1 - self.channel = channel - self.min_moist = min_moist - self.max_moist = max_moist - - async def measure(self): - val = await self.analog_accessor.read(self.channel) - self.moisture_val = val - - def moisture(self): - if self.moisture_val < 0: - raise RuntimeError("moisture() method called before measure()") - return int((self.moisture_val - self.min_moist) * 100 / (self.max_moist - self.min_moist)) - - \ No newline at end of file diff --git a/src/features/devices/device_factory.py b/src/features/devices/device_factory.py index a767931..d0538dc 100644 --- a/src/features/devices/device_factory.py +++ b/src/features/devices/device_factory.py @@ -26,7 +26,7 @@ def _create_item(self, config): raise NotImplementedError("Air device is not supported") if config.type == "soil": - if config.name == "sms": + if config.name == "sms" or config.name == "gravity_v1": mux_id = config.config["mux_id"] accessor = self.find_analog_accessor(mux_id) @@ -35,11 +35,7 @@ def _create_item(self, config): if config.type == "light": if config.name == "is": - - mux_id = int(config.config["mux_id"]) - accessor = self.find_analog_accessor(mux_id) - - return InsolationSensor(self.client, config, self.logger, accessor) + return InsolationSensor(self.client, config, self.logger) raise NotImplementedError(f"Device {config.type}-{config.name} is not supported") diff --git a/src/features/devices/drivers/adc_driver.py b/src/features/devices/drivers/adc_driver.py new file mode 100644 index 0000000..9d5215d --- /dev/null +++ b/src/features/devices/drivers/adc_driver.py @@ -0,0 +1,27 @@ +from features.analog_accessor.analog_accessor import AnalogAccessor + + +class AnalogDriver: + def __init__(self, analog_accessor: AnalogAccessor, channel: int): + self.analog_accessor = analog_accessor + self.channel = channel + self.min = 0 + self.max = 65535 + + async def read_raw(self): + return await self.analog_accessor.read(self.channel) + + async def read(self): + return await self._read() + + async def _read(self): + raw = await self.read_raw() + + if raw <= self.min: + return 0 + if raw >= self.max: + return 100 + + calc = int(raw / self.max * 100) + + return min(100, max(0, calc)) diff --git a/src/features/devices/light/drivers/default.py b/src/features/devices/light/drivers/default.py new file mode 100644 index 0000000..ff5e81c --- /dev/null +++ b/src/features/devices/light/drivers/default.py @@ -0,0 +1,27 @@ +from features.devices.drivers.adc_driver import AnalogDriver +from math import log, e +from machine import Pin, ADC +from uasyncio import sleep_ms + + +class InsolationDriver(AnalogDriver): + def __init__(self, adc_pin: int): + self.adc = ADC(Pin(adc_pin)) + self.light = 1000 + self.dark = 50000 + + async def read_raw(self): + await sleep_ms(1) + return self.adc.read_u16() + + async def _read(self): + raw = await self.read_raw() + + if raw >= self.dark: + return 0 + if raw <= self.light: + return 100 + + calc = int(276.295 - 24.188 * log(raw, e)) + + return min(100, max(0, calc)) diff --git a/src/features/devices/light/insolation_sensor.py b/src/features/devices/light/insolation_sensor.py index 38d25f8..cb04c2b 100644 --- a/src/features/devices/light/insolation_sensor.py +++ b/src/features/devices/light/insolation_sensor.py @@ -1,11 +1,10 @@ from features.devices.device import Device, DeviceConfig, ADCTopic from features.logger.logger import Logger from features.mqtt.mqtt import BaseMqttClient -from utime import ticks_ms, ticks_diff, time -from ujson import dumps, loads +from utime import ticks_ms from features.analog_accessor.analog_accessor import AnalogAccessor -import machine -from drivers.insolation import InsolationDriver + +from features.devices.light.drivers.default import InsolationDriver import uasyncio @@ -16,29 +15,23 @@ class Insolation(ADCTopic): class InsolationSensor(Device): def __init__(self, mqtt: BaseMqttClient, config: DeviceConfig, - logger: Logger, - analog_accessor: AnalogAccessor): + logger: Logger): super().__init__(mqtt, config, logger) data = config.config self._insolation = Insolation(mqtt, data["insolation"], logger) self.loop_span_ms = data["loopSpanMs"] - channel = data["channel"] + pin = data["adcPin"] - self.sensor = InsolationDriver(analog_accessor, channel) + self.sensor = InsolationDriver(pin) async def _update_config(self): pass async def _loop(self): current_time = ticks_ms() - await self.sensor.measure() + insolation = await self.sensor.read() - insolation = 100 - self.sensor.insolation() self._insolation.update(self.base_topic, current_time, insolation) - self.logger.debug(f"Insolation: {insolation}.") - await uasyncio.sleep_ms(self.loop_span_ms) - - diff --git a/src/features/devices/soil_humidity/drivers/default.py b/src/features/devices/soil_humidity/drivers/default.py new file mode 100644 index 0000000..a898d8e --- /dev/null +++ b/src/features/devices/soil_humidity/drivers/default.py @@ -0,0 +1,23 @@ +from features.analog_accessor.analog_accessor import AnalogAccessor +from features.devices.drivers.adc_driver import AnalogDriver +from math import log, e + + +class SMSDriver(AnalogDriver): + def __init__(self, analog_accessor: AnalogAccessor, channel: int): + super().__init__(analog_accessor, channel) + self.wet = 26000 + self.mid = 33000 + self.dry = 63000 + + async def _read(self): + raw = await self.read_raw() + + if raw >= self.dry: + return 0 + if raw <= self.wet: + return 100 + + calc = int(1162.08 - 105.507 * log(raw, e)) + + return min(100, max(0, calc)) diff --git a/src/features/devices/soil_humidity/drivers/gravity_v1.py b/src/features/devices/soil_humidity/drivers/gravity_v1.py new file mode 100644 index 0000000..36cbcbe --- /dev/null +++ b/src/features/devices/soil_humidity/drivers/gravity_v1.py @@ -0,0 +1,23 @@ +from features.analog_accessor.analog_accessor import AnalogAccessor +from features.devices.drivers.adc_driver import AnalogDriver +from math import log, e + + +class GravityV1(AnalogDriver): + def __init__(self, analog_accessor: AnalogAccessor, channel: int): + super().__init__(analog_accessor, channel) + self.wet = 23000 + self.mid = 37000 + self.dry = 50000 + + async def _read(self): + raw = await self.read_raw() + + if raw >= self.dry: + return 0 + if raw <= self.wet: + return 100 + + calc = int(1246.17 - 115.51 * log(raw, e)) + + return min(100, max(0, calc)) diff --git a/src/features/devices/soil_humidity/soil_moisture_sensor.py b/src/features/devices/soil_humidity/soil_moisture_sensor.py index 24add0c..7c592e0 100644 --- a/src/features/devices/soil_humidity/soil_moisture_sensor.py +++ b/src/features/devices/soil_humidity/soil_moisture_sensor.py @@ -1,11 +1,10 @@ from features.devices.device import Device, DeviceConfig, ADCTopic from features.logger.logger import Logger from features.mqtt.mqtt import BaseMqttClient -from utime import ticks_ms, ticks_diff, time -from ujson import dumps, loads from features.analog_accessor.analog_accessor import AnalogAccessor -import machine -from drivers.sms import SMSDriver +from features.devices.soil_humidity.drivers.default import SMSDriver +from features.devices.soil_humidity.drivers.gravity_v1 import GravityV1 +from utime import ticks_ms import uasyncio @@ -23,19 +22,20 @@ def __init__(self, mqtt: BaseMqttClient, self._moisture = Moisture(mqtt, data["moisture"], logger) self.loop_span_ms = data["loopSpanMs"] - channel = data["channel"] - self.sensor = SMSDriver(analog_accessor, channel) + self.sensor = self._create_sensor(analog_accessor, data["channel"]) + def _create_sensor(self, analog_accessor: AnalogAccessor, channel: int): + if self.config.name == "gravity_v1": + return GravityV1(analog_accessor, channel) + return SMSDriver(analog_accessor, channel) async def _update_config(self): pass async def _loop(self): current_time = ticks_ms() - await self.sensor.measure() - - moisture = self.sensor.moisture() + moisture = await self.sensor.read() self._moisture.update(self.base_topic, current_time, moisture) self.logger.debug(f"Moisture: {moisture}.") diff --git a/src/features/mqtt/mqtt.py b/src/features/mqtt/mqtt.py index 42c69d0..e5e8edf 100644 --- a/src/features/mqtt/mqtt.py +++ b/src/features/mqtt/mqtt.py @@ -19,7 +19,6 @@ def __init__(self, json: {}): self.client = json.get("client") self.ssl = json.get("ssl", True) self.keep_alive = self.value_in_range(name="keepAlive", min_v=10, max_v=120) - self.refresh_rate_ms = self.value_in_range(name="refreshRateMs", min_v=100, max_v=10000) class BaseMqttClient: diff --git a/src/main.py b/src/main.py index 46bdef1..824e1ff 100644 --- a/src/main.py +++ b/src/main.py @@ -7,7 +7,7 @@ from features.analog_accessor.analog_accessor_factory import AnalogAccessorFactory from features.devices.device_factory import DeviceFactory from features.logger.file_logger import FileLogger -from features.logger.logger_levels import LoggerLevels +from features.logger.logger import Logger from features.mqtt.mqtt_factory import MqttFactory from features.network.connection_factory import ConnectionFactory @@ -17,9 +17,9 @@ def setup_fail(logger: Logger, message: str, error_code: int): logger.error(message) + logger.error(str(error_code)) sleep(5) - sys.exit(error_code) - # in release change to reset() + reset() if __name__ == '__main__':