예제 #1
0
class Pmux:
    def __init__(self,
                 s0,
                 s1,
                 s2,
                 pin,
                 s3=None,
                 mux=None,
                 pin_direction="OUT",
                 pin_pull=None):
        """ It is possibile to initialize with:
            - pin numbers (or string on esp8266)
            - mux object and pin numbers (of mux pins)
            - Pin objects (either from machine or mux Pin objects [no mux object needed])
            :type mux: Mux object if a multiplexer is used
            :type pin: pin number or string (esp8266)
            :type pin_direction: str of pin_direction
            s3 is optional, only needed if 16 pins are used, 8 pins possible with s0-s2.
            pmux can be read like a list: value=amux[2]
            pmux can be set like a list: amux[2]=1
        """
        if mux:
            self.s0 = s0
            self.s1 = s1
            self.s2 = s2
            self.s3 = s3
            self.mux = mux
        else:
            if type(s0) in (int, str):
                self.s0 = PyPin(s0, machine.Pin.OUT)
                self.s1 = PyPin(s1, machine.Pin.OUT)
                self.s2 = PyPin(s2, machine.Pin.OUT)
                if s3:
                    self.s3 = PyPin(s3, machine.Pin.OUT)
            else:
                self.s0 = s0
                self.s1 = s1
                self.s2 = s2
                if s3:
                    self.s3 = s3
        if s3:
            self.__size = 16
        else:
            self.__size = 8
        self._selected_pin = None
        if pin_direction not in dir(machine.Pin):
            raise TypeError(
                "Pin_direction {!r} does not exist".format(pin_direction))
        if pin_pull not in dir(machine.Pin):
            raise TypeError("Pin_pull {!s} does not exist".format(pin_pull))
        self.pin = PyPin(pin, getattr(machine.Pin, pin_direction), pin_pull)

    def __getitem__(self, a):
        return self.value(a)

    def __setitem__(self, a, direction):
        return self.value(a, direction)

    def getSize(self):
        """ Get number of pins"""
        return self.__size

    def _selectPin(self, a):
        if a >= self.__size:
            raise ValueError("Maximum Port number is {!s}".format(self.__size -
                                                                  1))
        if type(self.s0) == int:  # mux pins
            if self.__size == 16:
                self.mux[self.s3] = (1 if a & 8 else 0)
            self.mux[self.s2] = (1 if a & 4 else 0)
            self.mux[self.s1] = (1 if a & 2 else 0)
            self.mux[self.s0] = (1 if a & 1 else 0)
            self.mux.write()
        else:
            if self.__size == 16:
                self.s3.value(1 if a & 8 else 0)
            self.s2.value(1 if a & 4 else 0)
            self.s1.value(1 if a & 2 else 0)
            self.s0.value(1 if a & 1 else 0)

    def value(self, a, value=None):
        if a != self._selected_pin:
            # only select pin if needed as it would slow IO-operations down and screw timings
            self._selectPin(a)
        if value is None:
            return self.pin.value()
        else:
            return self.pin.value(value)

    def mode(self, mode):
        if type(mode) == str:
            if mode not in dir(machine.Pin):
                raise TypeError("Mode {!r} is not available".format(mode))
            mode = getattr(machine.Pin, mode)
        self.pin.mode(mode)

    def pull(self, p=None):
        return self.pin.pull(p)

    def drive(self, d=None):
        return self.pin.drive(d)

    def init(self, *args, **kwargs):
        self.pin.init(*args, **kwargs)

    def Pin(self, p):
        return Pin(self, p)
예제 #2
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)
예제 #3
0
class EC(Component):
    DEBUG = False

    def __init__(self, r1, ra, adc, power_pin, ground_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):
        # This makes it possible to use multiple instances of MySensor
        global _unit_index
        _unit_index += 1
        super().__init__(COMPONENT_NAME, __version__, _unit_index)
        self._interval = interval or config.INTERVAL_SENSOR_PUBLISH
        self._prec_ec = int(precision_ec)
        self._adc = ADC(adc)
        self._ppin = Pin(power_pin, machine.Pin.OUT)
        self._gpin = Pin(ground_pin, machine.Pin.IN)  # changing to OUTPUT GND when needed
        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
        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, register=True):
        sens = '"unit_of_meas":"mS",' \
               '"val_tpl":"{{ value|float }}",'
        name = "{!s}{!s}{!s}".format(COMPONENT_NAME, self._count, "EC25")
        if register:
            await self._publishDiscovery(_COMPONENT_TYPE, self._topic_ec, name, sens,
                                         self._frn_ec or "EC25")
        else:
            await self._deleteDiscovery(_COMPONENT_TYPE, name)
        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, timeout=5):
        if time.ticks_diff(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._gpin.init(mode=machine.Pin.OUT)
        self._gpin.value(0)
        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
        self._gpin.init(mode=machine.Pin.IN)
        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
            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),
                                timeout=timeout, await_connection=False)
            await _mqtt.publish(self._topic_ppm, ppm, timeout=timeout, await_connection=False)
        return ec25, ppm

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

    async def ppm(self, publish=True, timeout=5):
        if time.ticks_diff(time.ticks_ms(), self._time) > 5000:
            await self._read(publish, timeout)
        return self._ppm