示例#1
0
class WaterSensor(ComponentSensor):
    DEBUG = False

    def __init__(self, adc, power_pin=None, cutoff_voltage=None, interval_publish=None,
                 interval_reading=1, mqtt_topic=None, friendly_name=None, discover=True,
                 expose_intervals=False, intervals_topic=None):
        interval_publish = interval_publish or -1
        global _unit_index
        _unit_index += 1
        super().__init__(COMPONENT_NAME, __version__, _unit_index, discover, interval_publish,
                         interval_reading, mqtt_topic, _log, expose_intervals, intervals_topic)
        self._adc = ADC(adc)
        self._ppin = Pin(power_pin, machine.Pin.OUT) if power_pin is not None else None
        self._cv = cutoff_voltage or self._adc.maxVoltage()
        self._lv = None
        self._addSensorType(SENSOR_BINARY_MOISTURE, 0, 0, VALUE_TEMPLATE, "", friendly_name,
                            mqtt_topic, None, True)
        self._pub_coro = None

    async def _read(self):
        a = time.ticks_us()
        p = self._ppin
        if p is not None:
            p.value(1)
        vol = self._adc.readVoltage()
        if self.DEBUG is True:
            print("#{!s}, V".format(self.getTopic(SENSOR_BINARY_MOISTURE)[-1]), vol)
        if p is not None:
            p.value(0)
        if vol >= self._cv:
            state = False
            if self._lv != state:
                # dry
                if self._pub_coro is not None:
                    asyncio.cancel(self._pub_coro)
                self._pub_coro = _mqtt.publish(self.getTopic(SENSOR_BINARY_MOISTURE), "OFF", qos=1,
                                               retain=True, timeout=None, await_connection=True)
                asyncio.get_event_loop().create_task(self._pub_coro)
            self._lv = state
        else:
            state = True
            if self._lv != state:
                # wet
                if self._pub_coro is not None:
                    asyncio.cancel(self._pub_coro)
                self._pub_coro = _mqtt.publish(self.getTopic(SENSOR_BINARY_MOISTURE), "ON", qos=1,
                                               retain=True, timeout=None, await_connection=True)
                asyncio.get_event_loop().create_task(self._pub_coro)
            self._lv = state
        b = time.ticks_us()
        if WaterSensor.DEBUG:
            print("Water measurement took", (b - a) / 1000, "ms")
示例#2
0
class EC(Component):
    DEBUG = False

    def __init__(self, r1, ra, adc, power_pin, ppm_conversion, temp_coef, k, temp_sensor,
                 precision_ec=3, interval=None, topic_ec=None, topic_ppm=None,
                 friendly_name_ec=None, friendly_name_ppm=None):
        super().__init__()
        self._interval = interval or config.INTERVAL_SEND_SENSOR
        self._prec_ec = int(precision_ec)
        self._adc = ADC(adc)
        self._ppin = Pin(power_pin, machine.Pin.OUT)
        self._r1 = r1
        self._ra = ra
        self._ppm_conversion = ppm_conversion
        self._temp_coef = temp_coef
        self._k = k
        self._temp = temp_sensor
        if hasattr(temp_sensor, "temperature") is False:
            raise AttributeError(
                "Temperature sensor {!s}, type {!s} has no async method temperature()".format(temp_sensor,
                                                                                              type(temp_sensor)))
        gc.collect()
        self._ec25 = None
        self._ppm = None
        self._time = 0
        # This makes it possible to use multiple instances of MySensor
        global _count
        self._count = _count
        _count += 1
        self._topic_ec = topic_ec or _mqtt.getDeviceTopic("{!s}/{!s}".format("EC", self._count))
        self._topic_ppm = topic_ppm or _mqtt.getDeviceTopic("{!s}/{!s}".format("PPM", self._count))
        self._frn_ec = friendly_name_ec
        self._frn_ppm = friendly_name_ppm

    async def _init(self):
        await super()._init()
        gen = self._read
        interval = self._interval
        while True:
            await gen()
            await asyncio.sleep(interval)

    async def _discovery(self):
        sens = '"unit_of_meas":"mS",' \
               '"val_tpl":"{{ value|float }}",'
        name = "{!s}{!s}{!s}".format(_component_name, self._count, "EC25")
        await self._publishDiscovery(_component_type, self._topic_ec, name, sens, self._frn_ec or "EC25")
        del sens, name
        gc.collect()
        sens = '"unit_of_meas":"ppm",' \
               '"val_tpl":"{{ value|int }}",'
        name = "{!s}{!s}{!s}".format(_component_name, self._count, "PPM")
        await self._publishDiscovery(_component_type, self._topic_ppm, name, sens, self._frn_ppm or "PPM")
        del sens, name
        gc.collect()

    async def _read(self, publish=True):
        if time.ticks_ms() - self._time < 5000:
            self._time = time.ticks_ms()
            await asyncio.sleep(5)
        temp = await self._temp.temperature(publish=False)
        if temp is None:
            await asyncio.sleep(3)
            temp = await self._temp.temperature(publish=False)
            if temp is None:
                _log.warn("Couldn't get temperature, aborting EC measurement")
                self._ec25 = None
                self._ppm = None
                return None, None
        self._ppin.value(1)
        vol = self._adc.readVoltage()
        # vol = self._adc.readVoltage()
        # micropython on esp is probably too slow to need this. It was intended for arduino
        if self.DEBUG is True:
            print("Temp", temp)
            print("V", vol)
        self._ppin.value(0)
        if vol >= self._adc.maxVoltage():
            ec25 = 0
            ppm = 0
            await _log.asyncLog("warn", "Cable not in fluid")
        else:
            if vol <= 0.5:
                _log.warn("Voltage <=0.5, change resistor")
            rc = (vol * (self._r1 + self._ra)) / (self._adc.maxVoltage() - vol)
            # rc = rc - self._ra # sensor connected to ground, not a pin, therefore no Ra
            ec = 1000 / (rc * self._k)
            ec25 = ec / (1 + self._temp_coef * (temp - 25.0))
            ppm = int(ec25 * self._ppm_conversion * 1000)
            ec25 = round(ec25, self._prec_ec)
            if self.DEBUG:
                print("Rc", rc)
                print("EC", ec)
                print("EC25", ec25, "MilliSimens")
                print("PPM", ppm)
        self._ec25 = ec25
        self._ppm = ppm
        self._time = time.ticks_ms()

        if publish:
            await _mqtt.publish(self._topic_ec, ("{0:." + str(self._prec_ec) + "f}").format(ec25))
            await _mqtt.publish(self._topic_ppm, ppm)
        return ec25, ppm

    async def ec(self, publish=True):
        if time.ticks_ms() - self._time > 5000:
            await self._read(publish)
        return self._ec25

    async def ppm(self, publish=True):
        if time.ticks_ms() - self._time > 5000:
            await self._read(publish)
        return self._ppm
示例#3
0
class EC(ComponentSensor):
    DEBUG = True

    def __init__(self,
                 r1,
                 ra,
                 rg,
                 adc,
                 power_pin,
                 ground_pin,
                 ppm_conversion,
                 temp_coef,
                 k,
                 temp_sensor: ComponentSensor,
                 read_timeout=400,
                 iterations=1,
                 precision_ec=3,
                 friendly_name_ec=None,
                 friendly_name_ppm=None,
                 **kwargs):
        # This makes it possible to use multiple instances of MySensor
        global _unit_index
        _unit_index += 1
        self.checkSensorType(temp_sensor, SENSOR_TEMPERATURE)
        super().__init__(COMPONENT_NAME,
                         __version__,
                         _unit_index,
                         logger=_log,
                         **kwargs)
        self._temp = temp_sensor
        self._addSensorType("ec", precision_ec, 0,
                            VALUE_TEMPLATE_JSON.format("ec|float"), "mS",
                            friendly_name_ec or "EC", self._topic,
                            DISCOVERY_EC)
        self._addSensorType("ppm", 0, 0, VALUE_TEMPLATE_JSON.format("ppm|int"),
                            "ppm", friendly_name_ppm or "PPM", self._topic,
                            DISCOVERY_PPM)

        self._adc = ADC(adc)
        self._ppin = Pin(power_pin,
                         machine.Pin.IN)  # changing to OUTPUT GND when needed
        self._gpin = Pin(ground_pin,
                         machine.Pin.IN)  # changing to OUTPUT GND when needed
        self._r1 = r1
        self._ra = ra
        self._rg = rg
        self._ppm_conversion = ppm_conversion
        self._temp_coef = temp_coef
        self._k = k
        self._to = read_timeout
        self._iters = iterations
        gc.collect()

    @micropython.native
    @staticmethod
    def _read_sync(_gpin_init, _ppin_init, _ppin_value, _adc_read, _in, ticks,
                   ticks_diff):
        a = ticks()
        _ppin_value(1)
        vol = _adc_read()
        b = ticks()
        # vol = _adc_read() # micropython on esp is way too slow to need this, it was for arduino.
        # _ppin_value(0) # if not using ppin as INPUT while not reading
        _gpin_init(_in)
        _ppin_init(_in)
        return vol, ticks_diff(b, a)

    async def _read(self):
        temp = await self._temp.getValue(SENSOR_TEMPERATURE)
        if temp is None:
            await asyncio.sleep(30)
            temp = await self._temp.getValue(SENSOR_TEMPERATURE)
            if temp is None:
                _log.warn("Couldn't get temperature, aborting EC measurement")
                return
        vols = []
        diffs = []
        adcs = []
        for _ in range(self._iters):
            self._gpin.init(machine.Pin.OUT, value=0)
            self._ppin.init(machine.Pin.OUT, value=0)
            adc, diff = self._read_sync(self._gpin.init, self._ppin.init,
                                        self._ppin.value, self._adc.read,
                                        machine.Pin.IN, time.ticks_us,
                                        time.ticks_diff)
            vol = self._adc.convertToVoltage(adc)
            vols.append(vol)
            adcs.append(adc)
            diffs.append(diff)
            if self._iters > 1:
                await asyncio.sleep(20)
        r = []
        for i, diff in enumerate(diffs):
            if diff > self._to:
                r.append(i)
            elif vols[i] >= self._adc.maxVoltage():
                r.append(i)
        if len(r) == len(diffs):
            vol = vols[0]
            adc = adcs[0]
            diff = diffs[0]
        else:
            for i in range(len(r) - 1, -1, -1):
                diffs.pop(r[i])
                adcs.pop(r[i])
                vols.pop(r[i])
            vol = sum(vols) / len(vols)
            adc = sum(adcs) / len(adcs)
            diff = sum(diffs) / len(diffs)
        if self.DEBUG:
            print("------------")
            print("Time", diff, "us")
            print("Temp", temp)
            print("V", vol, "ADC", adc)
            print("Vols", vols)
            print("adcs", adcs)
            print("diffs", diffs)
        if vol >= self._adc.maxVoltage():
            await self._setValue("ec", None, log_error=False)
            await self._setValue("ppm", None, log_error=False)
            await _log.asyncLog("warn", "Cable not in fluid")
        else:
            if vol <= 0.5:
                _log.warn("Voltage <=0.5, change resistor")
                await self._setValue("ec", None, log_error=False)
                await self._setValue("ppm", None, log_error=False)
                return
            rc = (vol * (self._r1 + self._ra)) / (3.3 - vol)
            rc = rc - self._rg
            ec = 1000 / (rc * self._k)
            ec25 = ec / (1 + self._temp_coef * (temp - 25.0))
            ppm = int(ec25 * self._ppm_conversion * 1000)
            if diff > self._to:
                await self._setValue("ec", None, log_error=False)
                await self._setValue("ppm", None, log_error=False)
                _log.warn(
                    "Reading timeout, discarding value {!s}V, {!s}ppm".format(
                        vol, ppm))
            else:
                await self._setValue("ec", ec25)
                await self._setValue("ppm", ppm)
                if self.DEBUG:
                    ec25 = round(ec25, 3)
                    print("Rc", rc)
                    print("EC", ec)
                    print("EC25", ec25, "MilliSimens")
                    print("PPM", ppm)
示例#4
0
class WaterSensor(Component):
    DEBUG = False

    def __init__(self, adc, power_pin=None, cutoff_voltage=None, interval=None,
                 interval_reading=1, topic=None, friendly_name=None):
        super().__init__()
        self._ir = interval_reading
        self._adc = ADC(adc)
        self._ppin = Pin(power_pin, machine.Pin.OUT) if power_pin is not None else None
        self._cv = cutoff_voltage or self._adc.maxVoltage()
        global _instances
        _instances.append(self)
        global _count
        self._t = topic or _mqtt.getDeviceTopic("waterSensor/{!s}".format(_count))
        self._count = _count
        _count += 1
        self._lv = None
        self._tm = time.ticks_ms()
        interval = interval or config.INTERVAL_SEND_SENSOR
        self._int = interval * 1000
        self._frn = friendly_name

    async def _init(self):
        await super()._init()
        if self._count == 0:  # only the first sensor reads all sensors to prevent uasyncio queue overflow
            interval_reading = self._ir - 0.05 * len(_instances)
            if interval_reading < 0:
                interval_reading = 0
                # still has 100ms after every read
            while True:
                for inst in _instances:
                    a = time.ticks_us()
                    await inst.water()
                    b = time.ticks_us()
                    if WaterSensor.DEBUG:
                        print("Water measurement took", (b - a) / 1000, "ms")
                    await asyncio.sleep_ms(50)
                    # using multiple sensors connected to Arduinos it would result in long blocking calls
                    # because a single call to a pin takes ~17ms
                await asyncio.sleep(interval_reading)

    async def _discovery(self):
        name = "{!s}{!s}".format(_component_name, self._count)
        sens = DISCOVERY_BINARY_SENSOR.format("moisture")  # device_class
        await self._publishDiscovery(_component_type, self._t, name, sens, self._frn or "Moisture")
        gc.collect()

    async def _read(self, publish=True):
        p = self._ppin
        if p is not None:
            p.value(1)
        vol = self._adc.readVoltage()
        if self.DEBUG is True:
            print("#{!s}, V".format(self._t[-1]), vol)
        if p is not None:
            p.value(0)
        if vol >= self._cv:
            state = False
            if publish is True and (time.ticks_diff(time.ticks_ms(), self._tm) > self._int or self._lv != state):
                await _mqtt.publish(self._t, "OFF", qos=1, retain=True)  # dry
                self._tm = time.ticks_ms()
            self._lv = state
            return False
        else:
            state = True
            if publish is True and (time.ticks_diff(time.ticks_ms(), self._tm) > self._int or self._lv != state):
                await _mqtt.publish(self._t, "ON", qos=1, retain=True)  # wet
                self._tm = time.ticks_ms()
            self._lv = state
            return True

    async def water(self, publish=True):
        return await self._read(publish)