kizniche / Mycodo

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

Winsen ZH03B Input Module Discussion #543

Closed kizniche closed 5 years ago

kizniche commented 6 years ago

With the latest code (ZH03B Module), I receive an exception. The sensor was working properly, then suddenly measurements stopped. I've attempted to execute each function of the class, however I cannot seem to produce a response (even with the fan stop/start commands).

Exception:

pi@tango:~/Mycodo $ env/bin/python ./mycodo/inputs/winsen_zh03b.py 
Reading sample
Exception while reading
Traceback (most recent call last):
  File "./mycodo/inputs/winsen_zh03b.py", line 136, in get_measurement
    pm_1_0, pm_2_5, pm_10_0 = self.QAReadSample()
  File "./mycodo/inputs/winsen_zh03b.py", line 214, in QAReadSample
    PM25 = int(self.HexToByte(((self.binascii.hexlify(self.ser.read(2))).hex())), 16)
ValueError: invalid literal for int() with base 16: ''
Traceback (most recent call last):
  File "./mycodo/inputs/winsen_zh03b.py", line 270, in <module>
    print("Channel 0: {}".format(ads.next()))
  File "./mycodo/inputs/winsen_zh03b.py", line 95, in next
    raise StopIteration  # required
StopIteration
kizniche commented 6 years ago

Currently there are lines that need to be uncommented if the script is going to be executed in the following way:

~/Mycodo/env/bin/python ~/Mycodo/mycodo/inputs/winsen_zh03b.py

The lines that need to be uncommented are:

https://github.com/kizniche/Mycodo/blob/60136a04284392ce54496f21faaf7fd4c5f5637d/mycodo/inputs/winsen_zh03b.py#L6-L9

kizniche commented 6 years ago

Update: The sleep/run commands actually do turn the fan on/off, however "FanERROR" is still being returned. Strange. It seems the response calculation may be the issue.

@Theoi-Meteoroi

kizniche commented 6 years ago

For reference, the code being used here is from https://github.com/Theoi-Meteoroi/Winsen_ZH03B

kizniche commented 6 years ago

I'm now getting readings. It appears the connection wasn't good. I pressed on the connector and it now is receiving data, not just sending it. However, it's still returning FanERROR when the turning the fan on and off is successful. The following are my debug lines:

pi@tango:~/Mycodo $ env/bin/python ./mycodo/inputs/winsen_zh03b.py 
TEST22: b'\xff\xa7'
TEST11: 66666137
TEST11: 66666137
TEST11: 66666137
TEST11: 66666137
RESP02: ffa7
TEST: FanERROR

TEST22 is what returns from self.ser.read(2) TEST11 is hexStr in HexToByte() RESP02 is response in QAReadSample()

kizniche commented 6 years ago

so, It may just be that "0000" was a placeholder response for a successful fan state change, or something happened to cause my sensor to return "ffa7" instead.

Theoi-Meteoroi commented 6 years ago

Hiya Kyle, I had a sense that I would need to look at that return code again. The sensor is supposed to return " FF A7 01 00 00 00 00 00 58" if the command succeeds and " FF A7 00 00 00 00 00 00 59" if it fails. I notice that I blithely read only two characters from the buffer. And the buffer probably has more. This command/response is pretty quick and the read buffer should be empty before the command is run to capture that response code correctly. I don't know why this code seemed to work when I wrote it - however it is clear to me that it needs changing.

So. Your sensor is reading that response just fine, but comparing the wrong bytes for success. We need to read one more byte, byte 3 has the 0x01 that would say "Got it!". Or we could change the compare bytes from "0000" to "FFA701" for success and "FFA700" for fail.

Something told me my test program was not testing everything I wanted to test. I put a modification below, and added input buffer flush to clean up the rest of the returned binary string.

Let me know if this helps. The reason I put the fanERROR in was so the caller could keep track of fan state. The fan must run or you get no readings after a short while. I'll keep an eye on this going forward. And I'll start working on fixing the repo.


def DormantMode(pwr_status):

#Turn dormant mode on or off. Must be on to measure.

# Turn fan off
#
if pwr_status == "sleep":
   ser.write( b"\xFF\x01\xA7\x01\x00\x00\x00\x00\x57")
   response = HexToByte( ((binascii.hexlify(ser.read(3))).hex()) )
   if response == "FFA701":
      ser.flushInput() #flush input buffer
      return ("FanOFF")
   else:
      ser.flushInput() #flush input buffer
      return ("FanERROR")

#  Turn fan on
#
if pwr_status == "run":
   ser.write( b"\xFF\x01\xA7\x00\x00\x00\x00\x00\x58")
   response = HexToByte( ((binascii.hexlify(ser.read(3))).hex()) )
   if response == "FFA701":
      ser.flushInput() #flush input buffer
      return ("FanON")
   else:
      ser.flushInput() #flush input buffer
      return ("FanERROR")

Theoi-Meteoroi commented 6 years ago

Need to send you the "improved" version of the USB-UART converter. I started using 0.10 spacing screw terminal blocks instead of those dupont connectors. Believe it or not - connectors are usually the most vexing item in my projects. I like to be able to build modules and harnesses to put things together neatly. This has lead to lots of what you are seeing - partially working hardware. So I'm going screw terminal blocks for anywhere I'm not using Grove type connectors. You would see I am using those in the Hackaday project I published.

kizniche commented 6 years ago

Thanks for the explanation and fix. Now that I can test, I'll be adding some more options, such as fan control, to the input options. The loose connection was actually the connector on the sensor itself, not the USB-UART converter. I'll disconnect it tomorrow and take a closer look at the connections to see if anything looks awry. Thanks again!

Theoi-Meteoroi commented 6 years ago

If there is any issue with that tiny connector, I can replace that cable. The method I use to build them yields 2x the cable assemblies because I cut the original in half. The ends are identical. Also note there is a vendor recommendation for how it gets mounted permanently - basically fan on the bottom to extend the life of the fan. This is the list at the bottom of v2.0 of the datasheet. The fan is exhaust and intake is the opposite port. That open port should be protected from stuff dropping into the enclosure or heavy mists settling.

Cautions:

  1. Do not change or displace any electronic components.
  2. Please avoid heavy shock and vibration
  3. The sensor should be vertical installed, to extend fan’s lifespan.
  4. Make sure that the air circulation of dust collecting holes is normal when installation.
  5. Please avoid sticky particles into the sensor to affect the sensor’s performance.
kizniche commented 6 years ago

The connector seems fine if I don't pull on it after I press it in fully. Good to know about the fan position. Now that the ability to turn the fan off or on (and select duration before measurement) in the module has been added, are there any other possible module features that would be beneficial?

Theoi-Meteoroi commented 5 years ago

There is a product that has some significant field exposure using this sensor type. The notable difference is that the unit contains TWO sensors - an acknowledgement that environmental contamination is a problem. I suspect that only one sensor operates at a time (one primary, the other backup) however I'm not certain. The fan control is probably utilized to implement that feature. The product itself is actually pretty expensive, probably to fund the website map infrastructure used for presentation.

Another take-away is the sensor enclosure/housing used - a simple 3" or 4" PVC end-cap with the opening to the bottom and a simple bracket for mounting. I'm gonna use that idea.