kizniche / Mycodo

An environmental monitoring and regulation system
http://kylegabriel.com/projects/
GNU General Public License v3.0
2.89k stars 486 forks source link

Cannot Connect to MH_Z19B Sensor - code fix attached #1315

Open tledwar opened 1 year ago

tledwar commented 1 year ago

This is related to https://forum.radicaldiy.com/t/mh-z19b-not-work-with-mycodo-python-but-works-with/1563 which was posted on the forum.

Mycodo Version: 8.15.9 OS: Linux raspberrypi 6.1.21+ #1642 armv6l GNU/Linux Platform: Pi Zero W

Issue:

Unable to get data from the sensor.

Reproduce Issue:

These items are mentioned in the above forum post and was part of the troubleshooting. I have not tried to not do these changes after I found a code fix. Reviewed https://forum.radicaldiy.com/t/mh-z19b-wiring-sanity-check/677 to properly wire up the sensor Disable Bluetooth and uninstal Py libraries /boot/config.txt:

Disable Bluetooth

dtoverlay=disable-bt [all] enable_uart=1

Create an Input for MH_Z19B

image

Testing was done with the following configured serial ports:

Although it is worth noting the following linked serial ports at the OS level: ls -la /dev/ser* lrwxrwxrwx 1 root tty 7 Jun 9 10:22 /dev/serial0 → ttyAMA0 lrwxrwxrwx 1 root root 5 Jun 9 10:22 /dev/serial1 → ttyS0

After testing, the proper port to use at least on this platform is /dev/serial0.

image

Once the proper port is configured and the system runs, the following error is generated:

2023-06-16 12:05:05,067 - ERROR - mycodo.inputs.mh_z19b_6a3d18a1 - UART Location: "/dev/serial0". 2023-06-16 12:05:05,177 - DEBUG - mycodo.controllers.controller_input_6a3d18a1 - get_measurement() found 2023-06-16 12:05:05,179 - DEBUG - mycodo - Input controller with ID 6a3d18a1-c77a-421b-8a71-8be78a91bf88 activated. 2023-06-16 12:05:05,185 - DEBUG - mycodo.controllers.controller_input_6a3d18a1 - listener() not found 2023-06-16 12:05:05,209 - INFO - mycodo.controllers.controller_input_6a3d18a1 - Activated in 5605.7 ms 2023-06-16 12:05:06,238 - DEBUG - mycodo.inputs.mh_z19b_6a3d18a1 - No response 2023-06-16 12:05:06,425 - DEBUG - mycodo.controllers.controller_input_6a3d18a1 - Adding measurements to InfluxDB with ID 6a3d18a1-c77a-421b-8a71-8be78a91bf88: {} 2023-06-16 12:05:07,993 - INFO - mycodo.controllers.controller_input_69d8bf11 - Activated in 1929.7 ms 2023-06-16 12:05:07,994 - DEBUG - mycodo - Input controller with ID 69d8bf11-ef3d-405e-b7be-5696fae74a07 activated.

No response error is coming from this section of the code from inputs/mh_z19b.py:

try:
            self.ser.flushInput()
            self.ser.write(bytearray([0xff, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79]))
            time.sleep(.01)
            resp = self.ser.read(9)

            if not resp:
                self.logger.debug("No response")
            elif len(resp) < 4:
                self.logger.debug("Too few values in response '{}'".format(resp))
            elif resp[0] != 0xff or resp[1] != 0x86:
                self.logger.error("Bad checksum")
            elif len(resp) >= 4:
                high = resp[2]
                low = resp[3]
                co2 = (high * 256) + low
                self.value_set(0, co2)
        except:
            self.logger.exception("get_measurement()")
        finally:
            self.measuring = False

Expected behavior

The system shall be able to read values from the sensor.

Potential Code Fix

After some comparison to https://github.com/UedaTakeyuki/mh-z19 (please note this library is mentioned several times in the Mycodo mh_z19b code), I discovered one key difference and it was the stop and start of a getter service. Whenever a connection was made to the serial port for this sensor, the third party library had a control feature to start/stop the service. The service was related to below:

$ systemctl --type=service --state=running | grep getty getty@tty1.service loaded active running Getty on tty1 serial-getty@serial0.service loaded active running Serial Getty on serial0 serial-getty@ttyAMA0.service loaded active running Serial Getty on ttyAMA0

I added these two methods to mh_z19b.py:

def start_getty():
  start_getty = ['sudo', 'systemctl', 'start', 'serial-getty@serial0.service']
  p = subprocess.call(start_getty)

def stop_getty():
  stop_getty = ['sudo', 'systemctl', 'stop', 'serial-getty@serial0.service']
  p = subprocess.call(stop_getty)

Then I added a call to these methods before and after the connection to the serial port in def initialize(self).

    def initialize(self):
        import serial

        self.logger.error('UART Location: "{dev}".'.format(
            dev=self.input_dev.uart_location))

        stop_getty()

        if is_device(self.input_dev.uart_location):
            try:
                self.ser = serial.Serial(
                    port=self.input_dev.uart_location,
                    baudrate=self.input_dev.baud_rate,
                    timeout=1,
                    writeTimeout=5)
            except serial.SerialException:
                self.logger.exception('Opening serial')
        else:
            self.logger.error('Could not open "{dev}". Check the device location is correct.'.format(
                dev=self.input_dev.uart_location))

        if self.abc_enable:
            self.abcon()
        else:
            self.abcoff()

        if self.measure_range:
            self.set_measure_range(self.measure_range)

        start_getty();

        time.sleep(0.1)

For the get_measurement(self) method, I had to do a little more work. First it was necessary to add a new connection to the device. This method was reusing the old connection and it did not seem to work. Second, I added a retry around the write command. This was used in the third party code. And finally, the stop/start Getty commands.

    def get_measurement(self):
        """Gets the MH-Z19's CO2 concentration in ppmv."""
        import serial

        if not self.ser:
            self.logger.error("Error 101: Device not set up. See https://kizniche.github.io/Mycodo/Error-Codes#error-101 for more info.")
            return

        self.return_dict = copy.deepcopy(measurements_dict)

        while self.calibrating:
            time.sleep(0.1)
        self.measuring = True

        stop_getty()

        if is_device(self.input_dev.uart_location):
            try:
                self.ser = serial.Serial(
                    port=self.input_dev.uart_location,
                    baudrate=self.input_dev.baud_rate,
                    timeout=1,
                    writeTimeout=5)
            except serial.SerialException:
                self.logger.exception('Error opening serial port')
        else:
            self.logger.error('Could not open "{dev}". Check the device location is correct.'.format(
                dev=self.input_dev.uart_location))

        try:
            self.logger.debug(self.ser.name)

            for retry in range(5):
                result = self.ser.write(bytearray([0xff, 0x01, 0x86, 0x00, 0x00, 0x00, 0x00, 0x00, 0x79]))
                resp = self.ser.read(9)

            self.logger.debug(len(resp))

            if not resp:
                self.logger.debug("No response")
            elif len(resp) < 4:
                self.logger.debug("Too few values in response '{}'".format(resp))
            elif resp[0] != 0xff or resp[1] != 0x86:
                self.logger.error("Bad checksum")
            elif len(resp) >= 4:
                high = resp[2]
                low = resp[3]
                co2 = (high * 256) + low
                self.value_set(0, co2)
        except:
            self.logger.exception(" Error getting data in get_measurement()")
        finally:
            self.measuring = False

        start_getty()

        return self.return_dict

Based on these code changes, I am actively receiving CO2 values using the input/output/functions defined at https://kylegabriel.com/projects/2021/09/mushroom-cultivation-automation.html

Complete file used during testing: mh_z19b.py.txt

kizniche commented 1 year ago

This issue has been mentioned on Radical DIY Forum. There might be relevant details there:

https://forum.radicaldiy.com/t/mh-z19b-not-work-with-mycodo-python-but-works-with/1563/3

kizniche commented 1 year ago

I don't think getty is necessary. How are you wiring your sensor?

tledwar commented 1 year ago

Yes that was me seeking help and I have cross posted this issue on that forum comment.


From: Kyle Gabriel @.> Sent: Friday, June 16, 2023 12:34:45 PM To: kizniche/Mycodo @.> Cc: Tony Edwards @.>; Author @.> Subject: Re: [kizniche/Mycodo] Cannot Connect to MH_Z19B Sensor - code fix attached (Issue #1315)

[EXTERNAL EMAIL: DO NOT CLICK links or attachments unless you recognize the sender and are expecting the email]

This issue has been mentioned on Radical DIY Forum. There might be relevant details there:

https://forum.radicaldiy.com/t/mh-z19b-not-work-with-mycodo-python-but-works-with/1563/3

— Reply to this email directly, view it on GitHubhttps://github.com/kizniche/Mycodo/issues/1315#issuecomment-1595020205, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ADXPIK3ZTKCOFN2GSRKGU7TXLSKLLANCNFSM6AAAAAAZJRIOFQ. You are receiving this because you authored the thread.Message ID: @.***>

------------------------------------------------------------------------------------ The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material, the disclosure of which is governed by applicable law. Any review, re-transmission, dissemination or other use of, or taking of any action in reliance upon, this information by persons or entities other than the intended recipient is prohibited. If you received this in error please contact the sender and destroy the materials contained in this message.

tledwar commented 1 year ago

I agree. I am not sure if Getty now comes with the OS or not. But I basically built my system following your growing guide online including parts, wiring and OS on the Pi. I will post pic of wiring when I am not mobile.


From: Kyle Gabriel @.> Sent: Sunday, June 18, 2023 1:55:32 PM To: kizniche/Mycodo @.> Cc: Tony Edwards @.>; Author @.> Subject: Re: [kizniche/Mycodo] Cannot Connect to MH_Z19B Sensor - code fix attached (Issue #1315)

[EXTERNAL EMAIL: DO NOT CLICK links or attachments unless you recognize the sender and are expecting the email]

I don't think getty is necessary. How are you wiring your sensor?

— Reply to this email directly, view it on GitHubhttps://github.com/kizniche/Mycodo/issues/1315#issuecomment-1596236749, or unsubscribehttps://github.com/notifications/unsubscribe-auth/ADXPIK7TC4MEF6ROCQPSCFLXL5FKJANCNFSM6AAAAAAZJRIOFQ. You are receiving this because you authored the thread.Message ID: @.***>

------------------------------------------------------------------------------------ The information transmitted is intended only for the person or entity to which it is addressed and may contain confidential and/or privileged material, the disclosure of which is governed by applicable law. Any review, re-transmission, dissemination or other use of, or taking of any action in reliance upon, this information by persons or entities other than the intended recipient is prohibited. If you received this in error please contact the sender and destroy the materials contained in this message.

tledwar commented 1 year ago

Connection is like this:

765d35a77294e0bfb98bd98d5a618c4f29b6e377

Sensor is correctly read with https://github.com/UedaTakeyuki/mh-z19 so it should be wired up correctly.