don't send 0 values master
authorDobrica Pavlinusic <dpavlin@rot13.org>
Sun, 10 Mar 2024 13:14:51 +0000 (14:14 +0100)
committerDobrica Pavlinusic <dpavlin@rot13.org>
Sun, 10 Mar 2024 13:14:51 +0000 (14:14 +0100)
24 files changed:
README
alsamixer-left-53-70.png [new file with mode: 0644]
audio2influx.sh [new file with mode: 0755]
ble-mijia.sh [new file with mode: 0755]
datasheets/MH-Z19B.pdf [new file with mode: 0644]
debian-install.sh [new file with mode: 0755]
dsm501.pl [new file with mode: 0755]
dust.sh [new file with mode: 0755]
iio2mqtt.pl [new file with mode: 0755]
mh-z19b.pl [new file with mode: 0755]
mq.sh [new file with mode: 0755]
mq7.sh [new file with mode: 0755]
pms3003.pl
system/air-dsm501.service [new file with mode: 0644]
system/air-mh-z19b.service [new file with mode: 0644]
system/air-mq.service [new file with mode: 0644]
system/air-mq7.service [new file with mode: 0644]
system/air-pms3003.service [new file with mode: 0644]
system/air-zph02.service [new file with mode: 0644]
system/audio2influx.service [new file with mode: 0644]
system/ble-mijia@.service [new file with mode: 0644]
system/dust.service [new file with mode: 0644]
system/iio2mqtt.service [new file with mode: 0644]
zph02.pl

diff --git a/README b/README
index aa1c861..65b31d5 100644 (file)
--- a/README
+++ b/README
@@ -1,6 +1,8 @@
 Experiments with air quality sensors
 
 Experiments with air quality sensors
 
-Scripts in this directory talk to sensors and store data into influxdb.
+Scripts in this directory read from sensor serial port output (which
+is 3.3V serial connected to usb serial dongle) and store data to
+influxdb using curl.
 
 When receiving data from sensors, they check  header and checksum to
 avoid sending garbage data. This also helps when you select wrong
 
 When receiving data from sensors, they check  header and checksum to
 avoid sending garbage data. This also helps when you select wrong
@@ -10,3 +12,24 @@ network latency to influxdb (which is somewhere in the cloud, and
 latency can vary just enough to create empty vertical stripes in
 graphs which is ugly and incorrect).
 
 latency can vary just enough to create empty vertical stripes in
 graphs which is ugly and incorrect).
 
+
+Exception to that rule is dsm501.pl which is general serial to
+influx bridge used by helper shell scripts to handle sensors
+which require 5V by connecting them to Arduino-like mcu.
+Example of that is:
+
+mq7.sh
+
+which uses https://github.com/dpavlin/mq7-co-monitor/
+
+on Arduino to produce output which is then fed to dsm501.pl.
+
+
+Another strage one is audio2influx.sh which doesn't require
+any external hardware but instead report sox info about 1s
+or audio to influxdb. I'm using this to passivly monitor
+fan rotation by connecting it directly to microphone input
+on netbook and adjusting input gain to prevent clipping
+using alsamixer.
+
+To make alsamixer work, I had to remove pulseaudio.
diff --git a/alsamixer-left-53-70.png b/alsamixer-left-53-70.png
new file mode 100644 (file)
index 0000000..af574ff
Binary files /dev/null and b/alsamixer-left-53-70.png differ
diff --git a/audio2influx.sh b/audio2influx.sh
new file mode 100755 (executable)
index 0000000..a8a49cc
--- /dev/null
@@ -0,0 +1,10 @@
+#!/bin/sh
+
+while true ; do
+
+arecord -d 1 -r 96000 /dev/shm/tmp.wav 2>/dev/null
+time=`date +'%s%N'`
+info=`sox /dev/shm/tmp.wav -n stat 2>&1 | grep '[0-9][0-9][0-9]*$' | sed -e 's/[()]//g' -e 's/: */=/' -e 's/  */_/g' | tee /dev/shm/audio | tr '\n' ',' | sed 's/[, ]*$//'`
+curl --silent -XPOST 'http://10.60.0.92:8086/write?consistency=any&db=rot13' --data-binary "fan,dc=trnjanska $info $time"
+
+done
diff --git a/ble-mijia.sh b/ble-mijia.sh
new file mode 100755 (executable)
index 0000000..908d63f
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/sh -e
+mac=A4:C1:38:90:DC:63
+test ! -z "$1" && mac=$1
+
+# based on https://ndimension.design.blog/2021/12/16/reading-data-from-xiaomi-mi-temperature-and-humidity-monitor-2-lywsd03mmc/
+# find sensor with: sudo hcitool lescan
+# A4:C1:38:D8:3F:9C LYWSD03MMC
+
+gatttool -b $mac --char-write-req --handle='0x0038' --value="0100" --listen | \
+       awk 'BEGIN { OFS=","; ORS="\n" } /value:/ { print "temperature=" strtonum("0x"$7$6) / 100, "humidity=" strtonum("0x"$8), "battery=" strtonum("0x"$10$9) / 1000 ; fflush() }' | \
+       xargs -i curl --silent -XPOST 'http://10.60.0.92:8086/write?consistency=any&db=rot13' --data-binary "mijia,dc=trnjanska,mac=$mac {}"
+
diff --git a/datasheets/MH-Z19B.pdf b/datasheets/MH-Z19B.pdf
new file mode 100644 (file)
index 0000000..9b762ae
Binary files /dev/null and b/datasheets/MH-Z19B.pdf differ
diff --git a/debian-install.sh b/debian-install.sh
new file mode 100755 (executable)
index 0000000..451f31a
--- /dev/null
@@ -0,0 +1,3 @@
+#!/bin/sh -xe
+
+apt install libdevice-serialport-perl libdata-dump-perl
diff --git a/dsm501.pl b/dsm501.pl
new file mode 100755 (executable)
index 0000000..0a237a7
--- /dev/null
+++ b/dsm501.pl
@@ -0,0 +1,44 @@
+#!/usr/bin/perl
+use warnings;
+use strict;
+# sudo apt install libdevice-serialport-perl libdata-dump-perl
+use Device::SerialPort;
+use Time::HiRes;
+use Data::Dump qw(dump);
+
+my $port = shift @ARGV || '/dev/serial/by-path/platform-3f980000.usb-usb-0:1.3:1.0-port0';
+my $influx_url = shift @ARGV || 'http://192.168.3.40:8086/write?consistency=any&db=rot13';
+my $measurement = $ENV{MEASUREMENT} || 'dsm501';
+
+my $s = new Device::SerialPort( $port ) || die $!;
+$s->baudrate(115200);
+$s->databits(8);
+$s->parity('none');
+$s->stopbits(1);
+$s->handshake('none');
+$s->read_char_time(0); # don't wait for each char
+$s->read_const_time(200); # ms for complete read
+
+
+while (1) {
+
+       my ($len, $string) = $s->read(128);
+       my $t = int( Time::HiRes::time() * 1_000_000_000 );
+       die $! if ! defined($len);
+       if ( $len > 0 ) {
+               #warn "# len=$len ",dump($string);
+               if ( $string !~ m/^#/ ) {
+                       $string =~ s/[\r\n]+$//;
+                       $string =~ s/\s/,/g;
+                       my $influx = "$measurement,dc=trnjanska $string $t";
+                       print "$influx\n" if -e '/dev/shm/air-debug';
+                       system "curl --silent -XPOST '$influx_url' --data-binary '$influx'"
+               } else {
+                       warn "SKIP: $string\n";
+               }
+       }
+
+};
+
+
+$s->close;
diff --git a/dust.sh b/dust.sh
new file mode 100755 (executable)
index 0000000..47c9b28
--- /dev/null
+++ b/dust.sh
@@ -0,0 +1,4 @@
+#!/bin/sh -xe
+
+cd /nuc/air-quality/
+MEASUREMENT=dust ./dsm501.pl /dev/ttyACM0
diff --git a/iio2mqtt.pl b/iio2mqtt.pl
new file mode 100755 (executable)
index 0000000..50668f0
--- /dev/null
@@ -0,0 +1,64 @@
+#!/usr/bin/perl
+use warnings;
+use strict;
+
+use Time::HiRes;
+
+# sudo apt install libiio-utils mosquitto-clients
+
+my $influx_url = shift @ARGV || 'http://192.168.3.40:8086/write?consistency=any&db=rot13';
+
+my $delay = $ENV{DELAY} || 1;
+
+my $hostname = `hostname -s`;
+chomp($hostname);
+
+while(1) {
+
+       my $t = Time::HiRes::time;
+       my $t_influx = int( $t * 1_000_000_000 );
+
+       my $iio = `iio_info`;
+
+       my $device;
+       my $name;
+
+       my $influx = '';
+
+       foreach ( split(/\n/, $iio) ) {
+               if ( m/iio:device\d+:\s+(\S+)/ ) {
+                       $device = $1;
+
+                       if ( $influx =~ m/,$/ ) {
+                               $influx =~ s/,$/ $t_influx\n/;
+                       } elsif ( $influx =~ m/ $/ ) { # only device
+                               $influx =~ s/\S+\s$//;
+                       }
+                       $influx .= "iio,dc=trnjanska,host=$hostname,device=$device ";
+
+               } elsif ( m/(\S+):\s+\(input\)/ ) {
+                       $name = $1;
+               } elsif ( m/attr\s+0:\s+input\svalue: (\d+[\.\d]+)/ ) {
+                       my $val = $1;
+                       if ( $val !~ m/\d+\.\d+/ ) {
+                               $val = $val / 1000;
+                       }
+                       my $topic = "iio/$hostname/$device/$name";
+                       #print "$topic $val\n";
+                       system "mosquitto_pub -h rpi2 -t $topic -m $val";
+
+                       $influx .= "$name=$val,";
+               } else {
+                       #warn "# $_\n";
+               }
+       }
+
+       $influx =~ s/,$/ $t_influx/;
+       system "curl --silent -XPOST '$influx_url' --data-binary '$influx'";
+       warn "$influx\n";
+
+       my $dt = Time::HiRes::time + $delay - $t - 0.01;
+       if ( $dt > 0 ) {
+               sleep $dt;
+       }
+}
diff --git a/mh-z19b.pl b/mh-z19b.pl
new file mode 100755 (executable)
index 0000000..52e3812
--- /dev/null
@@ -0,0 +1,59 @@
+#!/usr/bin/perl
+use warnings;
+use strict;
+# sudo apt install libdevice-serialport-perl libdata-dump-perl
+use Device::SerialPort;
+use Time::HiRes;
+use Data::Dump qw(dump);
+
+my $port = shift @ARGV || '/dev/serial/by-path/platform-3f980000.usb-usb-0:1.5:1.2';
+my $influx_url = shift @ARGV || 'http://192.168.3.40:8086/write?consistency=any&db=rot13';
+
+my $s = new Device::SerialPort( $port ) || die $!;
+$s->baudrate(9600);
+$s->databits(8);
+$s->parity('none');
+$s->stopbits(1);
+$s->handshake('none');
+$s->read_char_time(1);
+$s->read_const_time(10);
+$s->debug(1);
+
+$s->write("\xFF\x01\x86\x00\x00\x00\x00\x00\x79");
+
+while (1) {
+
+       my ($len, $string) = $s->read(9);
+       my $t = int( Time::HiRes::time() * 1_000_000_000 );
+       die $! if ! defined($len);
+       if ( $len > 0 ) {
+               my @v = unpack('C*', $string);
+               if ( $#v < 8 ) {
+                       die "# $len ",dump($string), dump( @v ), $/;
+               }
+
+               my $sum = 0;
+               foreach my $i ( 1 .. $#v - 1 ) {
+                       $sum += $v[$i];
+                       $sum = $sum & 0xff;
+               }
+               $sum = 0xff - $sum + 1;
+
+               my $checksum = $v[8];
+               my $co2 = $v[2] * 255 + $v[3];
+               if ( $v[0] == 0xff && $sum == $checksum ) {
+                       my $influx = "mh-z19b,dc=trnjanska co2=$co2 $t";
+                       print "$influx\n" if -e '/dev/shm/air-debug';
+                       system "curl --max-time 2 --silent -XPOST '$influx_url' --data-binary '$influx'";
+                       system "mosquitto_pub -h rpi2 -t 'air/mh-z19b/co2' -m $co2";
+               } else {
+                       die "checksum error";
+               }
+               sleep 1;
+               $s->write("\xFF\x01\x86\x00\x00\x00\x00\x00\x79");
+       }
+
+};
+
+
+$s->close;
diff --git a/mq.sh b/mq.sh
new file mode 100755 (executable)
index 0000000..83ba1f6
--- /dev/null
+++ b/mq.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+MEASUREMENT=mq /home/dpavlin/air-quality/dsm501.pl /dev/serial/by-path/pci-0000:00:1d.0-usb-0:2:1.0-port0 'http://10.60.0.92:8086/write?consistency=any&db=rot13' # debee
diff --git a/mq7.sh b/mq7.sh
new file mode 100755 (executable)
index 0000000..7713a6d
--- /dev/null
+++ b/mq7.sh
@@ -0,0 +1,4 @@
+#!/bin/sh -xe
+
+# https://github.com/dpavlin/mq7-co-monitor/
+MEASUREMENT=mq7 /home/pi/air-quality/dsm501.pl /dev/serial/by-path/platform-3f980000.usb-usb-0:1.2:1.0-port0
index 7c0205e..e90d1c0 100755 (executable)
@@ -6,9 +6,8 @@ use Device::SerialPort;
 use Time::HiRes;
 use Data::Dump qw(dump);
 
 use Time::HiRes;
 use Data::Dump qw(dump);
 
-my $port = shift @ARGV || '/dev/ttyUSB0';
-my $influx_url = shift @ARGV || 'http://10.13.37.229:8186/write?db=telegraf';
-$influx_url = 'http://10.13.37.92:8086/write?db=rot13';
+my $port = shift @ARGV || '/dev/serial/by-path/platform-3f980000.usb-usb-0:1.3:1.0-port0';
+my $influx_url = shift @ARGV || 'http://192.168.3.40:8086/write?consistency=any&db=rot13';
 
 my $debug = $ENV{DEBUG} || 0;
 
 
 my $debug = $ENV{DEBUG} || 0;
 
@@ -60,8 +59,8 @@ while (1) {
                        };
                        $influx =~ s/,$//;
                        $influx .= " $t";
                        };
                        $influx =~ s/,$//;
                        $influx .= " $t";
-                       print "$influx\n";
-                       system "curl --silent -XPOST '$influx_url' --data-binary '$influx'"
+                       print "$influx\n" if -e '/dev/shm/air-debug';
+                       system "curl --max-time 2 --silent -XPOST '$influx_url' --data-binary '$influx'"
                }
        }
 
                }
        }
 
diff --git a/system/air-dsm501.service b/system/air-dsm501.service
new file mode 100644 (file)
index 0000000..724fe2c
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=Air
+
+[Service]
+User=dpavlin
+ExecStart=/home/dpavlin/air-quality/dsm501.pl
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/system/air-mh-z19b.service b/system/air-mh-z19b.service
new file mode 100644 (file)
index 0000000..fd6e52e
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=Air
+
+[Service]
+User=pi
+ExecStart=/home/pi/air-quality/mh-z19b.pl
+Restart=always
+RestartSec=1s
+
+[Install]
+WantedBy=multi-user.target
diff --git a/system/air-mq.service b/system/air-mq.service
new file mode 100644 (file)
index 0000000..eed118f
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=Air
+
+[Service]
+User=dpavlin
+ExecStart=/home/dpavlin/air-quality/mq.sh
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/system/air-mq7.service b/system/air-mq7.service
new file mode 100644 (file)
index 0000000..2175f9c
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=Air
+
+[Service]
+User=pi
+ExecStart=/home/pi/air-quality/mq7.sh
+Restart=on-failure
+RestartSec=2s
+
+[Install]
+WantedBy=network-online.target
diff --git a/system/air-pms3003.service b/system/air-pms3003.service
new file mode 100644 (file)
index 0000000..a26ce98
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=Air
+
+[Service]
+User=pi
+ExecStart=/home/pi/air-quality/pms3003.pl
+Restart=on-failure
+RestartSec=2s
+
+[Install]
+WantedBy=network-online.target
diff --git a/system/air-zph02.service b/system/air-zph02.service
new file mode 100644 (file)
index 0000000..26e1c8f
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=Air
+
+[Service]
+User=pi
+ExecStart=/home/pi/air-quality/zph02.pl
+Restart=always
+RestartSec=3s
+
+[Install]
+WantedBy=multi-user.target
diff --git a/system/audio2influx.service b/system/audio2influx.service
new file mode 100644 (file)
index 0000000..670f9d4
--- /dev/null
@@ -0,0 +1,10 @@
+[Unit]
+Description=audio to influx
+
+[Service]
+User=dpavlin
+ExecStart=/home/dpavlin/air-quality/audio2influx.sh
+Restart=on-failure
+
+[Install]
+WantedBy=multi-user.target
diff --git a/system/ble-mijia@.service b/system/ble-mijia@.service
new file mode 100644 (file)
index 0000000..b12201c
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=Xiomi Mijia %i
+
+[Service]
+User=pi
+ExecStart=/home/pi/air-quality/ble-mijia.sh %i
+Restart=always
+RestartSec=3s
+
+[Install]
+WantedBy=network-online.target
diff --git a/system/dust.service b/system/dust.service
new file mode 100644 (file)
index 0000000..2744a77
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=dust sensor
+
+[Service]
+User=dpavlin
+ExecStart=/nuc/air-quality/dust.sh
+Restart=always
+RestartSec=2s
+
+[Install]
+WantedBy=multi-user.target
diff --git a/system/iio2mqtt.service b/system/iio2mqtt.service
new file mode 100644 (file)
index 0000000..95ebc8a
--- /dev/null
@@ -0,0 +1,11 @@
+[Unit]
+Description=iio2mqtt
+
+[Service]
+User=dpavlin
+ExecStart=/home/dpavlin/air-quality/iio2mqtt.pl
+Restart=always
+RestartSec=2s
+
+[Install]
+WantedBy=multi-user.target
index d0797d9..10f961e 100755 (executable)
--- a/zph02.pl
+++ b/zph02.pl
@@ -6,9 +6,8 @@ use Device::SerialPort;
 use Time::HiRes;
 use Data::Dump qw(dump);
 
 use Time::HiRes;
 use Data::Dump qw(dump);
 
-my $port = shift @ARGV || '/dev/ttyUSB0';
-my $influx_url = shift @ARGV || 'http://10.13.37.229:8186/write?db=telegraf';
-$influx_url = 'http://10.13.37.92:8086/write?db=rot13';
+my $port = shift @ARGV || '/dev/serial/by-path/platform-3f980000.usb-usb-0:1.5:1.0';
+my $influx_url = shift @ARGV || 'http://192.168.3.40:8086/write?consistency=any&db=rot13';
 
 my $s = new Device::SerialPort( $port ) || die $!;
 $s->baudrate(9600);
 
 my $s = new Device::SerialPort( $port ) || die $!;
 $s->baudrate(9600);
@@ -22,6 +21,11 @@ $s->read_const_time(10);
 
 while (1) {
 
 
 while (1) {
 
+       alarm 3;
+       # Usb serial which I'm using is buggy and blocks from time to time.
+       # This will ensure that we have passed here every 3 seconds
+       # or we will be killed and systemd will restart us
+
        my ($len, $string) = $s->read(9);
        my $t = int( Time::HiRes::time() * 1_000_000_000 );
        die $! if ! defined($len);
        my ($len, $string) = $s->read(9);
        my $t = int( Time::HiRes::time() * 1_000_000_000 );
        die $! if ! defined($len);
@@ -39,10 +43,11 @@ while (1) {
 
                my $checksum = $v[8];
                my $pcnt = $v[3] + ( $v[4] / 100 );
 
                my $checksum = $v[8];
                my $pcnt = $v[3] + ( $v[4] / 100 );
-               if ( $v[0] == 0xff && $sum == $checksum ) {
+               if ( $v[0] == 0xff && $sum == $checksum && $pcnt > 0) {
                        my $influx = "zph02,dc=trnjanska pm25_pcnt=$pcnt $t";
                        my $influx = "zph02,dc=trnjanska pm25_pcnt=$pcnt $t";
-                       print "$influx\n";
-                       system "curl --silent -XPOST '$influx_url' --data-binary '$influx'"
+                       print "$influx\n" if -e '/dev/shm/air-debug';
+                       system "curl --max-time 2 --silent -XPOST '$influx_url' --data-binary '$influx'";
+                       system "mosquitto_pub -h rpi2 -t 'air/zph02/pm25' -m $pcnt";
                }
        }
 
                }
        }