Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cannot read HPMA115C0 #26

Closed
cburlacu opened this issue Sep 24, 2021 · 9 comments
Closed

Cannot read HPMA115C0 #26

cburlacu opened this issue Sep 24, 2021 · 9 comments
Assignees
Labels
bug Something isn't working

Comments

@cburlacu
Copy link

cburlacu commented Sep 24, 2021

Hi,
There are some issues while trying to connect to this PM sensor:

commands = hpma115s0.commands._replace(
    passive_read=base.Cmd(  # Read Particle Measuring Results
        b"\x68\x01\x04\x93", b"\x40\x0d\x04", 16
    )
)

The length of the packet is 16 bytes, hence the second byte of the response should be 0x0D ... The datasheet seems to be wrong...

Also, when receiving the ack from passive mode command it fails (wrong header length - it receives 0xA5 0xA5) - it is validated as a message (in s0 _validate)...

diff --git a/src/pms/sensors/honeywell/hpma115c0.py b/src/pms/sensors/honeywell/hpma115c0.py
index b662658..4bcc6bb 100644
--- a/src/pms/sensors/honeywell/hpma115c0.py
+++ b/src/pms/sensors/honeywell/hpma115c0.py
@@ -11,7 +11,7 @@ from . import hpma115s0
 
 commands = hpma115s0.commands._replace(
     passive_read=base.Cmd(  # Read Particle Measuring Results
-        b"\x68\x01\x04\x93", b"\x40\x05\x04", 16
+        b"\x68\x01\x04\x93", b"\x40\x0d\x04", 16
     )
 )
 
@@ -21,6 +21,10 @@ class Message(hpma115s0.Message):
 
     data_records = slice(4)
 
+    @classmethod
+    def _validate(cls, message: bytes, header: bytes, length: int) -> base.Message:
+        return cls(message)
+
 
 @dataclass(frozen=False)
 class ObsData(base.ObsData):

It is not a fix, but at least I was able to take a sample

$ pms -s /dev/ttyUSB0 -m HPMA115C0 -n 10 -i 15 serial -f hexdump
00000000: 40 0d 04 00 04 00 07 00 09 00 17 00 00 00 00 84  @...............
00000010: 40 0d 04 00 03 00 04 00 04 00 04 00 00 00 00 a0  @...............
00000020: 40 0d 04 00 04 00 06 00 06 00 07 00 00 00 00 98  @...............
00000030: 40 0d 04 00 05 00 06 00 06 00 07 00 00 00 00 97  @...............
00000040: 40 0d 04 00 04 00 05 00 05 00 05 00 00 00 00 9c  @...............
00000050: 40 0d 04 00 05 00 06 00 06 00 08 00 00 00 00 96  @...............
00000060: 40 0d 04 00 05 00 07 00 07 00 08 00 00 00 00 94  @...............
00000070: 40 0d 04 00 05 00 07 00 07 00 08 00 00 00 00 94  @...............
00000080: 40 0d 04 00 04 00 06 00 06 00 07 00 00 00 00 98  @...............
00000090: 40 0d 04 00 04 00 06 00 07 00 08 00 00 00 00 96  @...............
@avaldebe avaldebe added the bug Something isn't working label Sep 27, 2021
@avaldebe avaldebe self-assigned this Sep 27, 2021
@avaldebe
Copy link
Owner

Hi @cburlacu

Thanks for the partial fix.

The length of the packet is 16 bytes, hence the second byte of the response should be 0x0D ... The datasheet seems to be wrong...

I do not have this sensor, so I wrote the module based on the datasheet...

It is not a fix, but at least I was able to take a sample

$ pms -s /dev/ttyUSB0 -m HPMA115C0 -n 10 -i 15 serial -f hexdump
00000000: 40 0d 04 00 04 00 07 00 09 00 17 00 00 00 00 84  @...............
00000010: 40 0d 04 00 03 00 04 00 04 00 04 00 00 00 00 a0  @...............
00000020: 40 0d 04 00 04 00 06 00 06 00 07 00 00 00 00 98  @...............
00000030: 40 0d 04 00 05 00 06 00 06 00 07 00 00 00 00 97  @...............
00000040: 40 0d 04 00 04 00 05 00 05 00 05 00 00 00 00 9c  @...............
00000050: 40 0d 04 00 05 00 06 00 06 00 08 00 00 00 00 96  @...............
00000060: 40 0d 04 00 05 00 07 00 07 00 08 00 00 00 00 94  @...............
00000070: 40 0d 04 00 05 00 07 00 07 00 08 00 00 00 00 94  @...............
00000080: 40 0d 04 00 04 00 06 00 06 00 07 00 00 00 00 98  @...............
00000090: 40 0d 04 00 04 00 06 00 07 00 08 00 00 00 00 96  @...............

Excellent! I can use this sample to update the test suite and hopefully fix this module.

I need to find some spare time to look into this, so pleas be patient.

avaldebe added a commit that referenced this issue Oct 18, 2021
@avaldebe
Copy link
Owner

avaldebe commented Oct 22, 2021

Hi @cburlacu

I pushed a patch a few days ago. Could you test this new version?
You can install it straight from github with the following command:

pip install --force-reinstall git+https://github.com/avaldebe/PyPMS.git@6409565

@cburlacu
Copy link
Author

Hi,

cezar@laptop: /tmp $ virtualenv -p $(which python3) venv     
created virtual environment CPython3.9.7.final.0-64 in 77ms
  creator CPython3Posix(dest=/tmp/venv, clear=False, no_vcs_ignore=False, global=False)
  seeder FromAppData(download=False, pip=bundle, setuptools=bundle, wheel=bundle, via=copy, app_data_dir=/home/cezar/.local/share/virtualenv)
    added seed packages: pip==21.2.4, setuptools==58.1.0, wheel==0.37.0
  activators BashActivator,CShellActivator,FishActivator,NushellActivator,PowerShellActivator,PythonActivator
cezar@laptop: /tmp $ source venv/bin/activate
(venv) cezar@laptop: /tmp $ pip install --force-reinstall git+https://github.com/avaldebe/PyPMS.git@6409565
Collecting git+https://github.com/avaldebe/PyPMS.git@6409565
  Cloning https://github.com/avaldebe/PyPMS.git (to revision 6409565) to ./pip-req-build-i9ux5xi1
  Running command git clone -q https://github.com/avaldebe/PyPMS.git /tmp/pip-req-build-i9ux5xi1
  WARNING: Did not find branch or tag '6409565', assuming revision or ref.
  Running command git checkout -q 6409565
  Resolved https://github.com/avaldebe/PyPMS.git to commit 6409565
  Installing build dependencies ... done
  Getting requirements to build wheel ... done
    Preparing wheel metadata ... done
Collecting importlib-metadata>=3.6
  Using cached importlib_metadata-4.8.1-py3-none-any.whl (17 kB)
Collecting typer>=0.3
  Using cached typer-0.4.0-py3-none-any.whl (27 kB)
Collecting pyserial>=3.0
  Using cached pyserial-3.5-py2.py3-none-any.whl (90 kB)
Collecting zipp>=0.5
  Using cached zipp-3.6.0-py3-none-any.whl (5.3 kB)
Collecting click<9.0.0,>=7.1.1
  Using cached click-8.0.3-py3-none-any.whl (97 kB)
Building wheels for collected packages: pypms
  Building wheel for pypms (PEP 517) ... done
  Created wheel for pypms: filename=pypms-0.6.1-py3-none-any.whl size=35945 sha256=b926f3a04ea3ae54da3f61d85b51c46219322d98662a3c964f465c957c648352
  Stored in directory: /tmp/pip-ephem-wheel-cache-y02an920/wheels/3c/62/15/56d88fa6e105e2fcc419fdccc82a4efe0b6965cab9c85275e1
Successfully built pypms
Installing collected packages: zipp, click, typer, pyserial, importlib-metadata, pypms
Successfully installed click-8.0.3 importlib-metadata-4.8.1 pypms-0.6.1 pyserial-3.5 typer-0.4.0 zipp-3.6.0
WARNING: You are using pip version 21.2.4; however, version 21.3 is available.
You should consider upgrading via the '/tmp/venv/bin/python -m pip install --upgrade pip' command.
(venv) cezar@laptop: /tmp $ which pms
/tmp/venv/bin/pms
(venv) cezar@laptop: /tmp $ pms -s /dev/ttyUSB1 -m HPMA115C0 -n 10 -i 15 serial -f hexdump
zsh: correct 'pms' to 'ps' [nyae]? n
Traceback (most recent call last):
  File "/tmp/venv/bin/pms", line 8, in <module>
    sys.exit(main())
  File "/tmp/venv/lib/python3.9/site-packages/typer/main.py", line 214, in __call__
    return get_command(self)(*args, **kwargs)
  File "/tmp/venv/lib/python3.9/site-packages/click/core.py", line 1128, in __call__
    return self.main(*args, **kwargs)
  File "/tmp/venv/lib/python3.9/site-packages/click/core.py", line 1053, in main
    rv = self.invoke(ctx)
  File "/tmp/venv/lib/python3.9/site-packages/click/core.py", line 1659, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/tmp/venv/lib/python3.9/site-packages/click/core.py", line 1395, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/tmp/venv/lib/python3.9/site-packages/click/core.py", line 754, in invoke
    return __callback(*args, **kwargs)
  File "/tmp/venv/lib/python3.9/site-packages/typer/main.py", line 500, in wrapper
    return callback(**use_params)  # type: ignore
  File "/tmp/venv/lib/python3.9/site-packages/pms/cli.py", line 84, in serial
    with reader:
  File "/tmp/venv/lib/python3.9/site-packages/pms/core/reader.py", line 124, in __enter__
    if not self.sensor.check(buffer, "passive_mode"):  # pragma: no cover
  File "/tmp/venv/lib/python3.9/site-packages/pms/core/sensor.py", line 87, in check
    self.Message.decode(buffer, self.command(command))
  File "/tmp/venv/lib/python3.9/site-packages/pms/sensors/base.py", line 59, in decode
    return cls.unpack(message, header, length)[cls.data_records]  # type: ignore[call-overload]
  File "/tmp/venv/lib/python3.9/site-packages/pms/sensors/base.py", line 41, in unpack
    msg = cls._validate(message, header, length)
  File "/tmp/venv/lib/python3.9/site-packages/pms/sensors/honeywell/hpma115s0.py", line 47, in _validate
    assert len(header) == 3, f"wrong header length {len(header)}"
AssertionError: wrong header length 2

As far as I remember the issue is that it would call validate for the start command response ack: i.e. 68 01 01 96 --> A5 A5 and this still fails...

@avaldebe
Copy link
Owner

Thanks for checking. I'll try to figure the problem

@avaldebe
Copy link
Owner

  File "/tmp/venv/lib/python3.9/site-packages/pms/sensors/honeywell/hpma115s0.py", line 47, in _validate
    assert len(header) == 3, f"wrong header length {len(header)}"
AssertionError: wrong header length 2

As far as I remember the issue is that it would call validate for the start command response ack: i.e. 68 01 01 96 --> A5 A5 and this still fails...

I see, it seems that the datasheet was inaccurate again and there is an ACK message before the measurement message.

avaldebe added a commit that referenced this issue Nov 16, 2021
avaldebe added a commit that referenced this issue Nov 16, 2021
@cburlacu
Copy link
Author

cburlacu commented Nov 16, 2021

Apparently still fails:

Traceback (most recent call last):
  File "/tmp/venv/bin/pms", line 8, in <module>
    sys.exit(main())
  File "/tmp/venv/lib/python3.9/site-packages/typer/main.py", line 214, in __call__
    return get_command(self)(*args, **kwargs)
  File "/tmp/venv/lib/python3.9/site-packages/click/core.py", line 1128, in __call__
    return self.main(*args, **kwargs)
  File "/tmp/venv/lib/python3.9/site-packages/click/core.py", line 1053, in main
    rv = self.invoke(ctx)
  File "/tmp/venv/lib/python3.9/site-packages/click/core.py", line 1659, in invoke
    return _process_result(sub_ctx.command.invoke(sub_ctx))
  File "/tmp/venv/lib/python3.9/site-packages/click/core.py", line 1395, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/tmp/venv/lib/python3.9/site-packages/click/core.py", line 754, in invoke
    return __callback(*args, **kwargs)
  File "/tmp/venv/lib/python3.9/site-packages/typer/main.py", line 500, in wrapper
    return callback(**use_params)  # type: ignore
  File "/tmp/venv/lib/python3.9/site-packages/pms/cli.py", line 84, in serial
    with reader:
  File "/tmp/venv/lib/python3.9/site-packages/pms/core/reader.py", line 124, in __enter__
    if not self.sensor.check(buffer, "passive_mode"):  # pragma: no cover
  File "/tmp/venv/lib/python3.9/site-packages/pms/core/sensor.py", line 87, in check
    self.Message.decode(buffer, self.command(command))
  File "/tmp/venv/lib/python3.9/site-packages/pms/sensors/base.py", line 59, in decode
    return cls.unpack(message, header, length)[cls.data_records]  # type: ignore[call-overload]
  File "/tmp/venv/lib/python3.9/site-packages/pms/sensors/base.py", line 41, in unpack
    msg = cls._validate(message, header, length)
  File "/tmp/venv/lib/python3.9/site-packages/pms/sensors/honeywell/hpma115s0.py", line 47, in _validate
    assert message == header
AssertionError

As far as I understand it, there are issued 2 commands and the result is concatenated:

        logger.debug(f"wake {self.sensor}")
        buffer = self._cmd("wake")
        self._pre_heat()
        buffer += self._cmd("passive_mode")
        logger.debug(f"buffer length: {len(buffer)}")

so in hpma115s0.py: _validate the message is b'\xa5\xa5\xa5\xa5' while the header is b'\xa5\xa5'...

Maybe buffer = self._cmd("passive_mode") if the check is only the for passive command: if not self.sensor.check(buffer, "passive_mode"): ? If needed add a separate check for the "wake" command before the "passive_mode"?

@KovairSoftware
Copy link

I happen to have one of these hooked up on my desk right now and it does seem to work:

DEBUG:pms:message hex: 400d04000000000000000000000000af
DEBUG:pms:message empty: warming up sensor
DEBUG:pms:message hex: 400d04000100030003000400000000a4
DEBUG:pms:message payload: (1, 3, 3, 4, 0, 0)
2022-05-26 17:16:46: PM1 1.0, PM2.5 3.0, PM4 3.0, PM10 4.0 μg/m3
DEBUG:pms:message hex: 400d04000100020002000200000000a8
DEBUG:pms:message payload: (1, 2, 2, 2, 0, 0)
2022-05-26 17:17:46: PM1 1.0, PM2.5 2.0, PM4 2.0, PM10 2.0 μg/m3
DEBUG:pms:message hex: 400d04000000010001000100000000ac
DEBUG:pms:message payload: (0, 1, 1, 1, 0, 0)
2022-05-26 17:18:46: PM1 0.0, PM2.5 1.0, PM4 1.0, PM10 1.0 μg/m3

One separate issue is that you're assuming zero values being returned mean the sensor is warming up. This isn't always the case. Here I had to wave an incense stick around for a few seconds in order to create enough pollution to get a reading. Let me know if you need any info to close the issue. I have a logic analyzer hooked up to it as well.

@avaldebe
Copy link
Owner

Hi @KovairSoftware

I happen to have one of these hooked up on my desk right now and it does seem to work:

That is great news. Thanks fort letting me know, so I can close the issue.

One separate issue is that you're assuming zero values being returned mean the sensor is warming up. This isn't always the case. Here I had to wave an incense stick around for a few seconds in order to create enough pollution to get a reading. Let me know if you need any info to close the issue. I have a logic analyzer hooked up to it as well.

I see it more like a feature. Some of the supported sensors report zero PM if they are queried too soon after powering up, and I one would have to manually filter out such measurements.

In any case, please open a separate issue about this "feature" so we can discuss how to address it.

Thanks again,
Á

@KovairSoftware
Copy link

I agree it's really a CR, I'll open it as you suggest. FYI it's been running 4 days without any issues. I appreciate your project!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

3 participants