Air Quality Sensor (PMS5003)

Grrr. You need import java.nio.* for ByteOrder. I thought it was in

Import javax.xml.bind.* to get the object. Then just call:


But unless you know that the incoming data is indeed Base-64 encoded (from your description and example I would say the answer is “no”) it is not going to do much for you. “�u q�” is not a Base-64 encoded string. There are no special characters in Base-64, only "0-1, a-z, A-Z, and ‘+’ and “/” are valid characters in a Base-64 encoded string.

Ya, I figured that part out, I have import java.nio.*, but its still giving me:

2016-04-05 14:14:53.534 [ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule 'Testing': The name 'ByteOrder' cannot be resolved to an item or type.

Code so far:

val ByteBuffer bb = ByteBuffer::wrap(Air_Quality.state.toString.getBytes)

val short first = bb.getShort
logInfo("Testing", "1st Bytes: " + first)
val short second = bb.getShort
logInfo("Testing", "2nd Bytes: " + second)
val short third = bb.getShort
logInfo("Testing", "3rd Bytes: " + third)
val short forth = bb.getShort
logInfo("Testing", "4th Bytes: " + forth)
val short fith = bb.getShort

P.S. Your right on Base64, I am changing the item tho to BASE64 so I don’t get junk in my log.

Try ByteOrder::LITTLE_ENDIAN or java.nio.ByteOrder::LITTLE_ENDIAN. Reference to something that is static needs to use “::” instead of “.”.

Though I looked it up and “first byte is the high eight bits and second byte the low eight bits” is big endian which is the default so you probably don’t need it anyway.

NOTE: your first value should be 0x424d = 16973 so your first long when it works should read “1st Bytes: 16973” , unless I did the math wrong.

Oh, your math is correct!!!

2016-04-05 14:41:57.524 [INFO ] [runtime.busevents ] - Air_Quality state updated to Qk0AHAACAAMABAACAAMABAVbAO+/vQATAAMAAgABcQACbA==

2016-04-05 14:41:57.525 [INFO ] [g.openhab.model.script.Testing] - 1st Bytes: 16973
2016-04-05 14:41:57.525 [INFO ] [g.openhab.model.script.Testing] - 2nd Bytes: 28
2016-04-05 14:41:57.525 [INFO ] [g.openhab.model.script.Testing] - 3rd Bytes: 2
2016-04-05 14:41:57.525 [INFO ] [g.openhab.model.script.Testing] - 4th Bytes: 3
2016-04-05 14:41:57.525 [INFO ] [g.openhab.model.script.Testing] - 5th Bytes: 4
2016-04-05 14:41:57.525 [INFO ] [g.openhab.model.script.Testing] - 6th Bytes: 2
2016-04-05 14:41:57.526 [INFO ] [g.openhab.model.script.Testing] - 7th Bytes: 3
2016-04-05 14:41:57.526 [INFO ] [g.openhab.model.script.Testing] - 8th Bytes: 4
2016-04-05 14:41:57.526 [INFO ] [g.openhab.model.script.Testing] - 9th Bytes: 1371
2016-04-05 14:41:57.526 [INFO ] [g.openhab.model.script.Testing] - 10th Bytes: 239
2016-04-05 14:41:57.526 [INFO ] [g.openhab.model.script.Testing] - 11th Bytes: -16451
2016-04-05 14:41:57.526 [INFO ] [g.openhab.model.script.Testing] - 12th Bytes: 19
2016-04-05 14:41:57.526 [INFO ] [g.openhab.model.script.Testing] - 13th Bytes: 3
2016-04-05 14:41:57.526 [INFO ] [g.openhab.model.script.Testing] - 14th Bytes: 2
2016-04-05 14:41:57.526 [INFO ] [g.openhab.model.script.Testing] - 15th Bytes: 1
2016-04-05 14:41:57.526 [INFO ] [g.openhab.model.script.Testing] - 16th Bytes: 28928

1 Like

How do I make the var an item? I tried:

postUpdate(Outside_PM1, bb.getShort)

And got a potUpdate?

2016-04-05 16:57:44.861 [ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule 'Testing': The name 'potUpdate(<XFeatureCallImplCustom>,<XMemberFeatureCallImplCustom>)' cannot be resolved to an item or type.

Then tried to just use pm1 var:

            val short pm1 = bb.getShort
            logInfo("Testing", "PM 1.0 : " + pm1)
            potUpdate(Outside_PM1, pm1)

And got the same error.

The postUpdate and sendCommand actions don’t like numbers, particularly primitive numbers. Try:


If that doesn’t work try:

val Number pm1 = new Number(bb.getShort)
postUpdate(Outside_PM1, pm1.toString)

First gives:
2016-04-05 17:38:01.182 [ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule 'Testing': The name '<XFeatureCallImplCustom>.potUpdate(<XFeatureCallImplCustom>)' cannot be resolved to an item or type.

2016-04-05 17:38:02.545 [ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule 'Testing': Could not invoke constructor: java.lang.Number.Number()

Again duh!

val Number pm1 = new Short(bb.getShort)

Number is abstract. You can’t create one using new.

I must be tired today.

2016-04-05 17:57:24.500 [ERROR] [o.o.c.s.ScriptExecutionThread ] - Error during the execution of rule ‘Testing’: Cannot create a duplicate value ‘pm1’.

Sorry, that was my bad

Good. I was scratching my head what that could have been.

Does this mean it works?

All good:

import javax.xml.bind.DatatypeConverter
import java.nio.*
import java.lang.*
import org.openhab.core.library.types.*

rule "Outside_Air_Quality"
	Item Outside_Air_Quality received update
	val to64 = DatatypeConverter::parseBase64Binary(Outside_Air_Quality.state.toString)
	val ByteBuffer bb = ByteBuffer::wrap(to64)
	val short start = bb.getShort
	val short length = bb.getShort
	if (start==16973 && length==28) {
		val short pm1 = bb.getShort
		postUpdate(Outside_PM1, pm1.toString)	
		val short pm25 = bb.getShort
		postUpdate(Outside_PM25, pm25.toString)	
		val short pm10 = bb.getShort
		postUpdate(Outside_PM10, pm10.toString)	

Hi Nathan,

If you have a moment can you provide an update on this part of your project?

  • How are the PMS5003 sensors working out, physically speaking? Are they durable and continuing to give accurate measurements?

  • How has the OpenHAB side of the setup been working? Have you further modified the code above in any way? Does this work with OpenHAB 2.x?

Appreciate any & all remarks, suggestions, comments on PMS5003 integration. I am looking to add 3 or 4 of these sensors to my home’s OpenHAB setup, as part of an air-filtration/HVAC control subsystem.

Thanks in advance.

I have 5 of them and they all are still working well including 1 outside in a covered but open box. I have not had to change anything in OpenHAB and I am running latest 2.x.

Thanks for the follow-up.

Can you describe the physical setup a bit? You’re using RS232 / 9600 baud output direct from the sensors, how does that get into openHAB?

What kind of wire / cable? Perhaps UTP?
What’s the longest cable run?
What kind of multi-port RS232 interface are you using on the openHAB server?


Can anyone tell me what this syntax is about:

.forEach[b | bts.append(Integer.toHexString(b)]

which is in one of the posts above.

As far as I can tell, forEach() can take a lambda as an argument. Is

[b | bts.append(Integer.toHexString(b)]

some type of lambda syntax? Can you point me to a link where that syntax is explained?

To me the | looks like java’s bitwise OR operator, which doesn’t make any sense to me in that code. Should that code really read:

.forEach(b -> bts.append(Integer.toHexString(b) )


The | is a separator between the arguments passed to the lambda and they body of the lambda.

Keep in mind that the Rules DSL is not Java but a domain specific language bearing many similarities with Xtend. For all syntax questions like this, the Rules page is a good place to start. Don’t neglect the links to the Xtend docs on that page though. The syntax deviates from “normal” standards in a number of ways.

Hey, thanks for the reply.

Ah. Okay. I was searching around Java for the syntax.

Yes, RS232 9600/8/N/1 into a RS232 TTL to USB converters from ebay. Wire is 2 pair 28 gauge, longest run is about 150 feet. The RS232 TTL to USB converters all plug into a 7 port USB hub.

I like to have my ports show up in the same order and with unique names so I have the following /etc/udev/rules.d/99-usb-serial.rules file:

SUBSYSTEM=="tty", KERNELS=="2-", SYMLINK+="ttyPort0", GROUP="dialout", MODE="0666"
SUBSYSTEM=="tty", KERNELS=="2-", SYMLINK+="ttyPort1", GROUP="dialout", MODE="0666"
SUBSYSTEM=="tty", KERNELS=="2-", SYMLINK+="ttyPort2", GROUP="dialout", MODE="0666"
SUBSYSTEM=="tty", KERNELS=="2-", SYMLINK+="ttyPort3", GROUP="dialout", MODE="0666"
SUBSYSTEM=="tty", KERNELS=="2-1.3.2", SYMLINK+="ttyPort4", GROUP="dialout", MODE="0666"
SUBSYSTEM=="tty", KERNELS=="2-1.3.3", SYMLINK+="ttyPort5", GROUP="dialout", MODE="0666"
SUBSYSTEM=="tty", KERNELS=="2-1.3.4", SYMLINK+="ttyPort6", GROUP="dialout", MODE="0666"
SUBSYSTEM=="tty", KERNELS=="2-1.4.4", SYMLINK+="zwave", GROUP="dialout", MODE:="0666"

If you use custom names you need /etc/default/openhab2 file:

/dev/ttyPort0:/dev/ttyPort1:/dev/ttyPort2:/dev/ttyPort3:/dev/ttyPort4:/dev/ttyPort5:/dev/ttyPort6 \
-Dfile.encoding=ISO-8859-1 -Duser.timezone=America/New_York"