#!/usr/bin/env perl use v5.32.1; use strict; use warnings; use FindBin qw($Bin); use lib "$Bin/../../../lib"; use lib "$Bin/../lib"; use FAPG::DAQ::EZO::USB qw( discover_ezo_usb_device normalize_probe_type open_ezo_usb read_probe_info read_once ); use FAPG::DAQ::MQTT qw(publish_mqtt publish_status_mqtt); use Sys::Hostname qw(hostname); =head1 NAME fapg-daq - read sensor data and send over MQTT =cut my $read_interval = positive_number_from_env( READ_INTERVAL => 5 ); my $retry_delay = positive_number_from_env( EZO_RECONNECT_INTERVAL => 5 ); my $expected_probe = expected_probe(); my ( $serial, $info, $device, $probe ); while (1) { if ( !defined $serial || !defined $info ) { ( $serial, $info ) = eval { $device = serial_device(); connect_probe($device); }; if ($@) { my $error = $@; warn "DAQ probe connection error: $error"; publish_probe_status( probe => $probe // $expected_probe, status => 'probe_error', error => $error, device => $device, message => 'Unable to connect to or interrogate EZO probe', ); close_serial($serial); ( $serial, $info, $device, $probe ) = (); sleep $retry_delay; next; } $probe = $info->{probe}; } my $reading = eval { read_once( $serial, probe => $info->{probe} ) }; if ($@) { my $error = $@; warn "DAQ probe read error, reconnecting to $device: $error"; publish_probe_status( probe => $probe // $expected_probe, status => 'probe_error', error => $error, device => $device, message => 'EZO probe did not return a valid reading', ); close_serial($serial); ( $serial, $info, $device, $probe ) = (); sleep $retry_delay; next; } say "$reading->{probe}: $reading->{value} $reading->{unit}"; eval { publish_mqtt($reading) } or warn "MQTT publish error: $@"; publish_probe_status( probe => $reading->{probe}, status => 'ok', device => $device, message => 'Probe reading valid', ); sleep $read_interval; } sub connect_probe { my ($device) = @_; my $serial = open_ezo_usb( device => $device ); my $info = eval { read_probe_info($serial) }; if ($@) { my $error = $@; close_serial($serial); die $error; } print <<"EOF"; Device $device Probe $info->{probe} Firmware $info->{firmware} Unit $info->{unit} EOF return ( $serial, $info ); } sub serial_device { return $ENV{EZO_SERIAL_DEVICE} if defined $ENV{EZO_SERIAL_DEVICE} && $ENV{EZO_SERIAL_DEVICE} ne q{}; if ( defined $ENV{SERIAL_DEVICE} && $ENV{SERIAL_DEVICE} ne q{} ) { return $ENV{SERIAL_DEVICE} if -e $ENV{SERIAL_DEVICE}; warn "Configured SERIAL_DEVICE=$ENV{SERIAL_DEVICE} does not exist; attempting USB serial discovery\n"; } return discover_ezo_usb_device(); } sub expected_probe { for my $name (qw(FAPG_DAQ_PROBE EZO_PROBE PROBE_TYPE)) { next if !defined $ENV{$name} || $ENV{$name} eq q{}; return normalize_probe_type( $ENV{$name} ); } my $hostname = hostname(); return $1 if $hostname =~ /(?:\A|-)(ph|do|ec|orp)(?:-|\z)/; return undef; } sub publish_probe_status { my (%status) = @_; if ( !defined $status{probe} || $status{probe} eq q{} ) { warn "DAQ status not published because probe type is unknown\n"; return; } eval { publish_status_mqtt( \%status ); 1 } or warn "MQTT status publish error: $@"; } sub close_serial { my ($serial) = @_; return if !defined $serial || !$serial->can('close'); eval { $serial->close; 1 } or warn "DAQ serial close error: $@"; } sub positive_number_from_env { my ( $name, $default ) = @_; my $value = $ENV{$name}; return $default if !defined $value || $value !~ /\A(?:\d+(?:\.\d*)?|\.\d+)\z/ || $value <= 0; return 0 + $value; }