From c74e629c42fe6b39f183957700b7aae3d0559703 Mon Sep 17 00:00:00 2001 From: s5uishida Date: Mon, 19 Aug 2019 01:55:39 +0900 Subject: [PATCH] first commit. --- .classpath | 7 + .project | 28 +++ .settings/org.eclipse.jdt.core.prefs | 7 + .settings/org.eclipse.pde.core.prefs | 4 + LICENSE | 21 ++ META-INF/MANIFEST.MF | 12 ++ README.md | 99 ++++++++- bin/.gitkeep | 0 build.properties | 5 + .../device/ppd42ns/driver/PPD42NSDriver.java | 191 ++++++++++++++++++ .../driver/PPD42NSObservationData.java | 62 ++++++ 11 files changed, 435 insertions(+), 1 deletion(-) create mode 100644 .classpath create mode 100644 .project create mode 100644 .settings/org.eclipse.jdt.core.prefs create mode 100644 .settings/org.eclipse.pde.core.prefs create mode 100644 LICENSE create mode 100644 META-INF/MANIFEST.MF create mode 100644 bin/.gitkeep create mode 100644 build.properties create mode 100644 src/io/github/s5uishida/iot/device/ppd42ns/driver/PPD42NSDriver.java create mode 100644 src/io/github/s5uishida/iot/device/ppd42ns/driver/PPD42NSObservationData.java diff --git a/.classpath b/.classpath new file mode 100644 index 0000000..eca7bdb --- /dev/null +++ b/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/.project b/.project new file mode 100644 index 0000000..9c103be --- /dev/null +++ b/.project @@ -0,0 +1,28 @@ + + + ppd42ns-driver + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 0000000..0c68a61 --- /dev/null +++ b/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 diff --git a/.settings/org.eclipse.pde.core.prefs b/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 0000000..e8ff8be --- /dev/null +++ b/.settings/org.eclipse.pde.core.prefs @@ -0,0 +1,4 @@ +eclipse.preferences.version=1 +pluginProject.equinox=false +pluginProject.extensions=false +resolve.requirebundle=false diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d1169d6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2019 Shigeru Ishida + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/META-INF/MANIFEST.MF b/META-INF/MANIFEST.MF new file mode 100644 index 0000000..972ce69 --- /dev/null +++ b/META-INF/MANIFEST.MF @@ -0,0 +1,12 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: java driver for ppd42ns - dust sensor module +Bundle-SymbolicName: ppd42ns-driver +Bundle-Version: 0.1.0 +Automatic-Module-Name: ppd42ns-driver +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 +Import-Package: com.pi4j.io.gpio;version="1.2.0", + com.pi4j.io.gpio.event;version="1.2.0", + org.slf4j;version="[1.7.25,1.8.0)" +Export-Package: io.github.s5uishida.iot.device.ppd42ns.driver;version="0.1.0" +Bundle-Vendor: Shigeru Ishida (s5uishida) diff --git a/README.md b/README.md index f4aec63..36928aa 100644 --- a/README.md +++ b/README.md @@ -1 +1,98 @@ -# ppd42ns-driver \ No newline at end of file +# ppd42ns-driver +ppd42ns-driver is a java library that operates dust sensor called [PPD42NS](http://wiki.seeedstudio.com/Grove-Dust_Sensor/) to connect PPD42NS to GPIO terminal of Raspberry Pi 3B and make it for use in java. +I releases this in the form of the Eclipse plug-in project. +You need Java 8 or higher. + +I use [Pi4J](http://wiki.seeedstudio.com/Grove-Dust_Sensor/) +for gpio communication in java and have confirmed that it works in Raspberry Pi 3B ([Raspbian Buster Lite OS](https://www.raspberrypi.org/downloads/raspbian/) (2019-07-10)). + +## Connection of PPD42NS and Raspberry Pi 3B +**Connect with `Vin <--> Vin`,`GND <--> GND`, `PM2.5 <--> Tx`.** +[This](https://github.com/mauricecyril/pidustsensor) is also helpful. +- `Pins` of [PPD42NS](http://wiki.seeedstudio.com/Grove-Dust_Sensor/) + - Vin (pin#3 - Red) + - GND (pin#1 - Black) + - PM2.5 (pin#4 - Yellow) +- [GPIO of Raspberry Pi 3B](https://www.raspberrypi.org/documentation/usage/gpio/README.md) + - Vin --> (4) + - GND --> (6) + - Tx --> (8) GPIO14 + +## Install Raspbian Buster Lite OS (2019-07-10) +The reason for using this version is that it is the latest as of July 2019 and [BlueZ](http://www.bluez.org/) 5.50 is included from the beginning, and use Bluetooth and serial communication simultaneously. + +## Configuration of Raspbian Buster Lite OS +- Edit `/boot/cmdline.txt` +``` +console=serial0,115200 --> removed +``` +- Edit `/boot/config.txt` +``` +@@ -55,6 +55,10 @@ + # Enable audio (loads snd_bcm2835) + dtparam=audio=on + ++enable_uart=1 ++dtoverlay=pi3-miniuart-bt ++core_freq=250 ++ + [pi4] + # Enable DRM VC4 V3D driver on top of the dispmanx display stack + dtoverlay=vc4-fkms-v3d +``` +When editing is complete, reboot. + +## Install WiringPi Native Library +Pi4J depends on the [WiringPi](http://wiringpi.com/) native library by Gordon Henderson. +The Pi4J native library is dynamically linked to WiringPi. +``` +# apt-get update +# apt-get install wiringpi +``` + +## Install jdk11 on Raspberry Pi 3B +For example, [jdk11 apt-install](https://apt.bell-sw.com/) at [BELLSOFT](https://bell-sw.com/) is shown below. +``` +# wget -q -O - https://download.bell-sw.com/pki/GPG-KEY-bellsoft | apt-key add - +# echo "deb [arch=armhf] https://apt.bell-sw.com/ stable main" | tee /etc/apt/sources.list.d/bellsoft.list +# apt-get update +# apt-get install bellsoft-java11 +``` + +## Install git +If git is not included, please install it. +``` +# apt-get install git +``` + +## Use this with the following bundles +- [SLF4J 1.7.26](https://www.slf4j.org/) +- [Pi4J 1.2 (pi4j-core.jar)](https://pi4j.com/download/pi4j-1.2.zip) + +I would like to thank the authors of these very useful codes, and all the contributors. + +## How to use +The following sample code will be helpful. +**In the following code, ppd42ns.read() takes about 30 seconds to measure.** +``` +import io.github.s5uishida.iot.device.ppd42ns.driver.PPD42NSDriver; +import io.github.s5uishida.iot.device.ppd42ns.driver.PPD42NSObservationData; + +public class MyPPD42NS { + private static final Logger LOG = LoggerFactory.getLogger(MyPPD42NS.class); + + public static void main(String[] args) { + PPD42NSDriver ppd42ns = PPD42NSDriver.getInstance(); + ppd42ns.open(); + + while (true) { + PPD42NSObservationData data = ppd42ns.read(); + LOG.info(data.toString()); + } + +// if (ppd42ns != null) { +// ppd42ns.close(); +// } + } +} +``` diff --git a/bin/.gitkeep b/bin/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/build.properties b/build.properties new file mode 100644 index 0000000..fc6ed0c --- /dev/null +++ b/build.properties @@ -0,0 +1,5 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + .,\ + LICENSE diff --git a/src/io/github/s5uishida/iot/device/ppd42ns/driver/PPD42NSDriver.java b/src/io/github/s5uishida/iot/device/ppd42ns/driver/PPD42NSDriver.java new file mode 100644 index 0000000..243713b --- /dev/null +++ b/src/io/github/s5uishida/iot/device/ppd42ns/driver/PPD42NSDriver.java @@ -0,0 +1,191 @@ +package io.github.s5uishida.iot.device.ppd42ns.driver; + +import java.util.Date; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.pi4j.io.gpio.GpioController; +import com.pi4j.io.gpio.GpioFactory; +import com.pi4j.io.gpio.GpioPinDigitalInput; +import com.pi4j.io.gpio.Pin; +import com.pi4j.io.gpio.PinPullResistance; +import com.pi4j.io.gpio.PinState; +import com.pi4j.io.gpio.RaspiGpioProvider; +import com.pi4j.io.gpio.RaspiPin; +import com.pi4j.io.gpio.RaspiPinNumberingScheme; +import com.pi4j.io.gpio.event.GpioPinDigitalStateChangeEvent; +import com.pi4j.io.gpio.event.GpioPinListenerDigital; + +/* + * Refer to http://wiki.seeedstudio.com/Grove-Dust_Sensor/ + * + * @author s5uishida + * + */ +public class PPD42NSDriver { + private static final Logger LOG = LoggerFactory.getLogger(PPD42NSDriver.class); + + private final Pin GPIO_IN = RaspiPin.GPIO_14; + private final int OBSERVE_TIMEOUT_MILLIS = 30000; + private final int GPIO_IN_TIMEOUT_MILLIS = 50000; + + private final GpioController gpio; + + private GpioPinDigitalInput diPin; + private String logPrefix; + + private final AtomicInteger useCount = new AtomicInteger(0); + private final BlockingQueue queue = new LinkedBlockingQueue(); + + private static PPD42NSDriver ppd42ns; + + private PPD42NSGpioPinListenerDigital ppd42nsListener; + + synchronized public static PPD42NSDriver getInstance() { + if (ppd42ns == null) { + ppd42ns = new PPD42NSDriver(); + } + return ppd42ns; + } + + private PPD42NSDriver() { + GpioFactory.setDefaultProvider(new RaspiGpioProvider(RaspiPinNumberingScheme.BROADCOM_PIN_NUMBERING)); + gpio = GpioFactory.getInstance(); + logPrefix = "[" + getName() + "] "; + } + + synchronized public void open() { + try { + LOG.debug(logPrefix + "before - useCount:{}", useCount.get()); + if (useCount.compareAndSet(0, 1)) { + diPin = gpio.provisionDigitalInputPin(GPIO_IN, PinPullResistance.PULL_DOWN); + diPin.setShutdownOptions(true); + ppd42nsListener = new PPD42NSGpioPinListenerDigital(this, queue); + diPin.addListener(ppd42nsListener); + LOG.info(logPrefix + "opened SPI SLCK."); + } + } finally { + LOG.debug(logPrefix + "after - useCount:{}", useCount.get()); + } + } + + synchronized public void close() { + try { + LOG.debug(logPrefix + "before - useCount:{}", useCount.get()); + if (useCount.compareAndSet(1, 0)) { + diPin.removeAllListeners(); + gpio.unprovisionPin(diPin); + gpio.shutdown(); + LOG.info(logPrefix + "closed SPI SLCK."); + } + } finally { + LOG.debug(logPrefix + "after - useCount:{}", useCount.get()); + } + } + + public String getName() { + return GPIO_IN.getName().replaceAll("\\s", "_"); + } + + public String getLogPrefix() { + return logPrefix; + } + + public PPD42NSObservationData read() { + queue.clear(); + ppd42nsListener.start(); + try { + return queue.poll(GPIO_IN_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + } catch (InterruptedException e) { + LOG.warn("caught - {}", e.toString()); + return null; + } + } + + /****************************************************************************************************************** + * Sample main + ******************************************************************************************************************/ + public static void main(String[] args) { + PPD42NSDriver ppd42ns = PPD42NSDriver.getInstance(); + ppd42ns.open(); + + while (true) { + PPD42NSObservationData data = ppd42ns.read(); + LOG.info(data.toString()); + } + +// if (ppd42ns != null) { +// ppd42ns.close(); +// } + } + + class PPD42NSGpioPinListenerDigital implements GpioPinListenerDigital { + private final Logger LOG = LoggerFactory.getLogger(PPD42NSGpioPinListenerDigital.class); + + private final PPD42NSDriver ppd42ns; + private final BlockingQueue queue; + + private Date startTime; + private long startLowTimeMillis; + private long totalLowTimeMillis; + + public PPD42NSGpioPinListenerDigital(PPD42NSDriver ppd42ns, BlockingQueue queue) { + this.ppd42ns = ppd42ns; + this.queue = queue; + } + + public void reset() { + startTime = null; + startLowTimeMillis = -1; + totalLowTimeMillis = 0; + } + + public void start() { + reset(); + startTime = new Date(); + } + + private float pcs2ugm3(float pcs) { + double density = 1.65 * Math.pow(10, 12); + double r25 = 0.44 * Math.pow(10, -6); + double vol25 = (4 / 3) * Math.PI * Math.pow(r25, 3); + double mass25 = density * vol25; + double K = 3531.5; + return (float)(pcs * K * mass25); + } + + @Override + public void handleGpioPinDigitalStateChangeEvent(GpioPinDigitalStateChangeEvent event) { + LOG.trace(ppd42ns.getLogPrefix() + "{} -> {}", event.getPin(), event.getState()); + + if (startTime == null) { + return; + } + + Date currentTime = new Date(); + if (event.getState() == PinState.LOW) { + startLowTimeMillis = currentTime.getTime(); + return; + } else if (event.getState() == PinState.HIGH && startLowTimeMillis > 0) { + totalLowTimeMillis += currentTime.getTime() - startLowTimeMillis; + } else { + return; + } + + if ((currentTime.getTime() - startTime.getTime()) >= OBSERVE_TIMEOUT_MILLIS) { + float ratio = 100f * ((float)totalLowTimeMillis) / ((float)(currentTime.getTime() - startTime.getTime())); + float pcs = (float)(1.1 * Math.pow(ratio, 3) - 3.8 * Math.pow(ratio, 2) + 520.0 * ratio + 0.62); + float ugm3 = pcs2ugm3(pcs); + PPD42NSObservationData data = new PPD42NSObservationData(startTime, currentTime, pcs, ugm3); + queue.offer(data); + LOG.trace(ppd42ns.getLogPrefix() + "offer - {}", data.toString()); + reset(); + } + } + } +} diff --git a/src/io/github/s5uishida/iot/device/ppd42ns/driver/PPD42NSObservationData.java b/src/io/github/s5uishida/iot/device/ppd42ns/driver/PPD42NSObservationData.java new file mode 100644 index 0000000..23280e4 --- /dev/null +++ b/src/io/github/s5uishida/iot/device/ppd42ns/driver/PPD42NSObservationData.java @@ -0,0 +1,62 @@ +package io.github.s5uishida.iot.device.ppd42ns.driver; + +import java.text.SimpleDateFormat; +import java.util.Date; + +/* + * @author s5uishida + * + */ +public class PPD42NSObservationData { + private static final String dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"; + private static final SimpleDateFormat sdf = new SimpleDateFormat(dateFormat); + + private final Date startTime; + private final Date endTime; + private final float pcs; + private final float ugm3; + + public PPD42NSObservationData(Date startTime, Date endTime, float pcs, float ugm3) { + this.startTime = startTime; + this.endTime = endTime; + this.pcs = pcs; + this.ugm3 = ugm3; + } + + public Date getStartTime() { + return startTime; + } + + public String getStartTimeString() { + return sdf.format(startTime); + } + + public Date getEndTime() { + return endTime; + } + + public String getEndTimeString() { + return sdf.format(endTime); + } + + public long getObservationTimeMillis() { + return endTime.getTime() - startTime.getTime(); + } + + public float getPcs() { + return pcs; + } + + public float getUgm3() { + return ugm3; + } + + @Override + public String toString() { + return "startTime:" + sdf.format(startTime) + + " endTime:" + sdf.format(endTime) + + " observationTimeMillis:" + (endTime.getTime() - startTime.getTime()) + + " pcs:" + pcs + + " ugm3:" + ugm3; + } +}