summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfishsoupisgood <github@madingley.org>2021-01-12 16:58:31 +0000
committerfishsoupisgood <github@madingley.org>2021-01-12 16:58:31 +0000
commit7c6887eaaf812b63bab6c5e134f80a2ef36aeb31 (patch)
tree4b4b0d371107ae1b8540ca1618cb9aa796b72616
parentf4b573fe337a436d5e2b20be4be031d77376d609 (diff)
downloadheating-7c6887eaaf812b63bab6c5e134f80a2ef36aeb31.tar.gz
heating-7c6887eaaf812b63bab6c5e134f80a2ef36aeb31.tar.bz2
heating-7c6887eaaf812b63bab6c5e134f80a2ef36aeb31.zip
works
-rw-r--r--.gitignore1
-rwxr-xr-xboiler-monster/mr3020/etc/stm32/startup5
-rwxr-xr-xboiler-monster/mr3020/usr/bin/thermostat14
-rw-r--r--heating-cgi/.gitignore3
-rw-r--r--heating-cgi/Makefile22
-rwxr-xr-xheating-cgi/boiler_rx (renamed from munin/boiler_rx)4
-rwxr-xr-xheating-cgi/heating.cgi707
-rwxr-xr-xheating-cgi/run_boiler_rx6
-rw-r--r--munin/.gitignore1
-rwxr-xr-xmunin/boiler552
-rw-r--r--prometheus/.gitignore3
-rw-r--r--prometheus/Makefile11
l---------prometheus/home/httpd/html/weatherstation/updateweatherstation.php1
-rwxr-xr-xprometheus/home/httpd/html/weatherstation/updateweatherstation.pl83
-rwxr-xr-xprometheus/usr/local/bin/make_habitable11
-rwxr-xr-xprometheus/usr/local/bin/midnight_tidy15
-rwxr-xr-xprometheus/usr/local/bin/set_radiator_target47
-rwxr-xr-xprometheus/usr/local/bin/vent17
-rw-r--r--radiator-plc/stm32/Makefile.rules2
-rw-r--r--radiator-plc/stm32/app/Makefile9
-rw-r--r--radiator-plc/stm32/app/control.c114
-rw-r--r--radiator-plc/stm32/app/logic.c178
-rw-r--r--radiator-plc/stm32/app/main.c4
-rw-r--r--radiator-plc/stm32/app/project.h1
-rw-r--r--radiator-plc/stm32/app/prototypes.h1
-rw-r--r--radiator-plc/stm32/app/rain.c100
-rw-r--r--radiator-plc/stm32/app/ticker.c5
-rwxr-xr-xset_radiator_target10
-rwxr-xr-xtasmota-config/configure-radiators12
-rwxr-xr-xwatch6
30 files changed, 1105 insertions, 840 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bb96c79
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+NOT
diff --git a/boiler-monster/mr3020/etc/stm32/startup b/boiler-monster/mr3020/etc/stm32/startup
index 9422745..ca19e1d 100755
--- a/boiler-monster/mr3020/etc/stm32/startup
+++ b/boiler-monster/mr3020/etc/stm32/startup
@@ -3,7 +3,11 @@
logger -t stm32 "startup"
LOCK=/var/lock/LCK..ttyATH0
+LOCK2=/var/lock/LCK..ttyATH0.stm32
echo $$ > ${LOCK}.tmp
+mv -f ${LOCK}.tmp ${LOCK}
+echo $$ > ${LOCK2}.tmp
+mv -f ${LOCK2}.tmp ${LOCK2}
#killall -9 ser2net
@@ -48,3 +52,4 @@ logger -t stm32 "Booting"
stm32flash -g 0 "${PORT}"
rm -f ${LOCK}
+rm -f ${LOCK2}
diff --git a/boiler-monster/mr3020/usr/bin/thermostat b/boiler-monster/mr3020/usr/bin/thermostat
index 1689964..af99a4a 100755
--- a/boiler-monster/mr3020/usr/bin/thermostat
+++ b/boiler-monster/mr3020/usr/bin/thermostat
@@ -1,10 +1,12 @@
#!/bin/sh
-LOCK=/var/lock/LCK..ttyATH0
+LOCK=/var/lock/LCK..ttyATH0.stm32
M=10.32.139.1
WATERS=0
-MAX=70
+MAX=65
+
+OUTSIDE="$(mosquitto_sub -t tele/weather/tempc -h ${M} -W 1 -C 1 | sed -e 's/\..*$//g') "
# conventions
# POWER is power to valve coil
@@ -30,13 +32,15 @@ done
#... others where we care about the delta
-for i in kstudy_radiator bedroom_radiator spare_bedroom_radiator dd_radiator1 dd_radiator2 dd_radiator3 hall_radiator kitchen_radiator music_room_radiator; do
+for i in kstudy_radiator bedroom_radiator spare_bedroom_radiator dd_radiator1 dd_radiator2 dd_radiator3 hall_radiator kitchen_radiator music_room_radiator 2fl_stair_radiator 2fl_main_radiator; do
O="$(mosquitto_sub -t stat/$i/OPEN -h ${M} -W 1 -C 1)"
- D="$(mosquitto_sub -t stat/$i/DELTA -h ${M} -W 1 -C 1 | sed -e 's/\..*$//g') "
+ T="$(mosquitto_sub -t stat/$i/TEMPERATURE -h ${M} -W 1 -C 1 | sed -e 's/\..*$//g') "
+
+ D="$(expr $T - $OUTSIDE)"
W=0
if [ "$O" == "1" ]; then
- W=$[ $D * 3 + 40]
+ W="$( expr \( \( \( $D * 2 \) / 3 \) + 55 \) )"
fi
logger -t thermostat " $i O=$O D=$D W=$W"
diff --git a/heating-cgi/.gitignore b/heating-cgi/.gitignore
new file mode 100644
index 0000000..574889a
--- /dev/null
+++ b/heating-cgi/.gitignore
@@ -0,0 +1,3 @@
+*.stamp
+*.bak
+*.swp
diff --git a/heating-cgi/Makefile b/heating-cgi/Makefile
new file mode 100644
index 0000000..7a7bc3b
--- /dev/null
+++ b/heating-cgi/Makefile
@@ -0,0 +1,22 @@
+
+foo: other.stamp prometheus.stamp
+
+SRC=heating.cgi boiler_rx run_boiler_rx
+
+
+other.stamp: ${SRC}
+ scp heating.cgi other:/export/home/httpd/heating/cgi-bin/index.cgi
+ scp boiler_rx run_boiler_rx other:/usr/local/sbin/
+ touch $@
+
+prometheus.stamp: ${SRC}
+ scp heating.cgi prometheus:/home/httpd/html/heating.cgi
+ scp boiler_rx run_boiler_rx dhoma:/usr/local/sbin/
+ touch $@
+
+clean:
+ /bin/rm -f *.bak
+
+tidy:
+ perltidy -b heating.cgi boiler_rx
+
diff --git a/munin/boiler_rx b/heating-cgi/boiler_rx
index f794999..777b3ee 100755
--- a/munin/boiler_rx
+++ b/heating-cgi/boiler_rx
@@ -1,12 +1,12 @@
#!/usr/bin/env perl
use Net::Telnet;
-use Data::Dumper;
+use IO::File;
sub dump_file($$) {
my ( $name, $guts ) = @_;
- $fh = new IO::File ">" . $name;
+ my $fh = new IO::File ">" . $name;
return unless defined $fh;
$fh->print($guts);
$fh->close;
diff --git a/heating-cgi/heating.cgi b/heating-cgi/heating.cgi
new file mode 100755
index 0000000..88474dd
--- /dev/null
+++ b/heating-cgi/heating.cgi
@@ -0,0 +1,707 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+use Net::MQTT::Simple;
+use JSON::Parse;
+
+use Data::Dumper;
+
+use CGI qw/:standard :cgi-lib/;
+use CGI::Carp qw(fatalsToBrowser);
+
+package Boiler {
+
+ use IO::File;
+
+ #0 Status => 0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ #17 Relative modulation level => 0
+ #18 CH water pressure => 1.14453125
+ #25 Boiler water temperature => 53
+ #26 DHW temperature => 49
+ #28 Return water temperature => 48.375
+ #57 Max CH water setpoint => 80
+ #80 Supply inlet temperature => 21.25
+ #256 Status => 0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
+ #257 Control setpoint => 7
+ #270 Maximum relative modulation level => 100
+ #272 Room setpoint => 18
+ #280 Room temperature => 26.7265625
+ #312 DHW setpoint => 60
+
+ my $ot_registers = {
+ 0 => {
+ format => ["flag16"],
+ name => "Status",
+ names => [
+ '', '',
+ '', '',
+ '', '',
+ 'DHW enabled', 'CH enabled',
+ '', 'diagnostic indication',
+ '', '',
+ 'Flame', 'DHW active',
+ 'CH active', 'fault'
+ ],
+ warnings => [
+ undef, undef, undef, undef, undef, undef, undef, undef,
+ undef, undef, undef, undef, undef, undef, undef, '1'
+ ]
+ },
+ 1 => {
+ format => ["f8.8"],
+ name => "Control setpoint",
+ units => 'C',
+ graphs => { '1' => '', '3' => '' }
+ },
+ 2 => { format => [ "flag8", "u8" ], name => "Master configuration" },
+ 3 => { format => [ "flag8", "u8" ], name => "Slave configuration" },
+ 4 => { format => [ "u8", "u8" ], name => "Remote command" },
+ 5 =>
+ { format => [ "flag8", "u8" ], name => "Application-specific flags" },
+ 6 =>
+ { format => [ "flag8", "flag8" ], name => "Remote parameter flags" },
+ 7 => { format => ["f8.8"], name => "Cooling control signal" },
+ 8 => { format => ["f8.8"], name => "Control setpoint 2" },
+ 9 => { format => ["f8.8"], name => "Remote override room setpoint" },
+ 10 => { format => [ "u8", "nu" ], name => "Number of TSPs" },
+ 11 => { format => [ "u8", "u8" ], name => "TSP setting" },
+ 12 => { format => [ "u8", "nu" ], name => "Size of fault buffer" },
+ 13 => { format => [ "u8", "u8" ], name => "Fault buffer entry" },
+ 14 => {
+ format => ["f8.8"],
+ name => "Maximum relative modulation level",
+ units => '%'
+ },
+ 15 => {
+ format => [ "u8", "u8" ],
+ name => "Boiler capacity and modulation limits"
+ },
+ 16 => {
+ format => ["f8.8"],
+ name => "Room setpoint",
+ units => 'C',
+ graphs => { '3' => '' }
+ },
+ 17 => {
+ format => ["f8.8"],
+ name => "Relative modulation level",
+ units => '%'
+ },
+ 18 => {
+ format => ["f8.8"],
+ name => "CH water pressure",
+ units => 'bar',
+ warning => '0.9:2.6',
+ critical => '0.8:2.8'
+ },
+ 19 => { format => ["f8.8"], name => "DHW flow rate" },
+ 20 => { format => ["time"], name => "Day of week and time of day" },
+ 21 => { format => ["date"], name => "Date" },
+ 22 => { format => ["u16"], name => "Year" },
+ 23 => { format => ["f8.8"], name => "Room Setpoint CH2" },
+ 24 => {
+ format => ["f8.8"],
+ name => "Room temperature",
+ units => 'C',
+ graphs => { '3' => '' }
+ },
+ 25 => {
+ format => ["f8.8"],
+ name => "Boiler water temperature",
+ units => 'C',
+ graphs => { '1' => '', '2' => '' }
+ },
+ 26 => {
+ format => ["f8.8"],
+ name => "DHW temperature",
+ units => 'C',
+ graphs => { '2' => '' }
+ },
+ 27 => { format => ["f8.8"], name => "Outside temperature" },
+ 28 => {
+ format => ["f8.8"],
+ name => "Return water temperature",
+ units => 'C',
+ graphs => { '1' => '' }
+ },
+ 29 => { format => ["f8.8"], name => "Solar storage temperature" },
+ 30 => { format => ["f8.8"], name => "Solar collector temperature" },
+ 31 => { format => ["f8.8"], name => "Flow temperature CH2" },
+ 32 => { format => ["f8.8"], name => "DHW2 temperature" },
+ 33 => { format => ["s16"], name => "Exhaust temperature" },
+ 34 =>
+ { format => ["f8.8"], name => "Boiler heat exchanger temperature" },
+ 35 =>
+ { format => [ "u8", "u8" ], name => "Boiler fan speed and setpoint" },
+ 48 => { format => [ "s8", "s8" ], name => "DHW setpoint boundaries" },
+ 49 =>
+ { format => [ "s8", "s8" ], name => "Max CH setpoint boundaries" },
+ 50 => {
+ format => [ "s8", "s8" ],
+ name => "OTC heat curve ratio boundaries"
+ },
+ 51 =>
+ { format => [ "s8", "s8" ], name => "Remote parameter 4 boundaries" },
+ 52 =>
+ { format => [ "s8", "s8" ], name => "Remote parameter 5 boundaries" },
+ 53 =>
+ { format => [ "s8", "s8" ], name => "Remote parameter 6 boundaries" },
+ 54 =>
+ { format => [ "s8", "s8" ], name => "Remote parameter 7 boundaries" },
+ 55 =>
+ { format => [ "s8", "s8" ], name => "Remote parameter 8 boundaries" },
+ 56 => {
+ format => ["f8.8"],
+ name => "DHW setpoint",
+ units => 'C',
+ graphs => { '2' => '' }
+ },
+ 57 => {
+ format => ["f8.8"],
+ name => "Max CH water setpoint",
+ units => 'C',
+ graphs => { '1' => '' }
+ },
+ 58 => { format => ["f8.8"], name => "OTC heat curve ratio" },
+ 59 => { format => ["f8.8"], name => "Remote parameter 4" },
+ 60 => { format => ["f8.8"], name => "Remote parameter 5" },
+ 61 => { format => ["f8.8"], name => "Remote parameter 6" },
+ 62 => { format => ["f8.8"], name => "Remote parameter 7" },
+ 63 => { format => ["f8.8"], name => "Remote parameter 8" },
+ 70 => { format => [ "flag8", "flag8" ], name => "Status V/H" },
+ 71 => { format => [ "nu", "u8" ], name => "Control setpoint V/H" },
+ 72 => { format => [ "flag8", "u8" ], name => "Fault flags/code V/H" },
+ 73 => { format => ["u16"], name => "OEM diagnostic code V/H" },
+ 74 =>
+ { format => [ "flag8", "u8" ], name => "Configuration/memberid V/H" },
+ 75 => { format => ["f8.8"], name => "OpenTherm version V/H" },
+ 76 => { format => [ "u8", "u8" ], name => "Product version V/H" },
+ 77 => { format => [ "nu", "u8" ], name => "Relative ventilation" },
+ 78 =>
+ { format => [ "u8", "u8" ], name => "Relative humidity exhaust air" },
+ 79 => { format => ["u16"], name => "CO2 level exhaust air" },
+ 80 => {
+ format => ["f8.8"],
+ name => "Supply inlet temperature",
+ units => 'C',
+ graphs => { '2' => '' }
+ },
+ 81 => { format => ["f8.8"], name => "Supply outlet temperature" },
+ 82 => { format => ["f8.8"], name => "Exhaust inlet temperature" },
+ 83 => { format => ["f8.8"], name => "Exhaust outlet temperature" },
+ 84 => { format => ["u16"], name => "Exhaust fan speed" },
+ 85 => { format => ["u16"], name => "Inlet fan speed" },
+ 86 => {
+ format => [ "flag8", "flag8" ],
+ name => "Remote parameter settings V/H"
+ },
+ 87 => { format => [ "u8", "nu" ], name => "Nominal ventilation value" },
+ 88 => { format => [ "u8", "nu" ], name => "Number of TSPs V/H" },
+ 89 => { format => [ "u8", "u8" ], name => "TSP setting V/H" },
+ 90 => { format => [ "u8", "nu" ], name => "Size of fault buffer V/H" },
+ 91 => { format => [ "u8", "u8" ], name => "Fault buffer entry V/H" },
+ 100 =>
+ { format => [ "nu", "flag8" ], name => "Remote override function" },
+ 101 => {
+ format => [ "flag8", "flag8" ],
+ name => "Solar storage mode and status"
+ },
+ 102 =>
+ { format => [ "flag8", "u8" ], name => "Solar storage fault flags" },
+ 103 => {
+ format => [ "flag8", "u8" ],
+ name => "Solar storage config/memberid"
+ },
+ 104 =>
+ { format => [ "u8", "u8" ], name => "Solar storage product version" },
+ 105 =>
+ { format => [ "u8", "nu" ], name => "Number of TSPs solar storage" },
+ 106 =>
+ { format => [ "u8", "u8" ], name => "TSP setting solar storage" },
+ 107 => {
+ format => [ "u8", "u8" ],
+ name => "Size of fault buffer solar storage"
+ },
+ 108 => {
+ format => [ "u8", "u8" ],
+ name => "Fault buffer entry solar storage"
+ },
+ 113 => { format => ["u16"], name => "Unsuccessful burner starts" },
+ 114 => { format => ["u16"], name => "Flame signal too low count" },
+ 115 => { format => ["u16"], name => "OEM diagnostic code" },
+ 116 => { format => ["u16"], name => "Burner starts" },
+ 117 => { format => ["u16"], name => "CH pump starts" },
+ 118 => { format => ["u16"], name => "DHW pump/valve starts" },
+ 119 => { format => ["u16"], name => "DHW burner starts" },
+ 120 => { format => ["u16"], name => "Burner operation hours" },
+ 121 => { format => ["u16"], name => "CH pump operation hours" },
+ 122 => { format => ["u16"], name => "DHW pump/valve operation hours" },
+ 123 => { format => ["u16"], name => "DHW burner operation hours" },
+ 124 => { format => ["f8.8"], name => "OpenTherm version Master" },
+ 125 => { format => ["f8.8"], name => "OpenTherm version Slave" },
+ 126 => { format => [ "u8", "u8" ], name => "Master product version" },
+ 127 => { format => [ "u8", "u8" ], name => "Slave product version" },
+ };
+
+ my $ot_types = {
+ 0 => "read data",
+ 1 => "write data",
+ 2 => "invalidate data",
+ 3 => "unknown 0x3",
+ 4 => "read ack",
+ 5 => "write ack",
+ 6 => "data invalid",
+ 7 => "unknown 0x7"
+ };
+
+ sub fetch_file($) {
+ my ($name) = @_;
+
+ my $fh = new IO::File "<" . $name;
+ return undef unless defined $fh;
+ local $/;
+ my $guts = $fh->getline;
+ $fh->close;
+ undef $fh;
+
+ return $guts;
+ }
+
+ sub decode($$$) {
+ my ( $dataref, $string, $offset ) = @_;
+ my $b0 = hex( substr( $string, 0, 2 ) );
+
+ my $type = ( $b0 >> 4 ) & 7;
+ my $id = hex( substr( $string, 2, 2 ) );
+
+ my $data = pack( 'H4', substr( $string, 4, 4 ) );
+ my $format = ["u16"];
+
+ my $ret = {
+ id => $id,
+ fake_id => $id + $offset,
+ type => $type,
+ type_str => $ot_types->{$type},
+ };
+
+ my $bits = undef;
+
+ if ( exists $ot_registers->{$id} ) {
+ $ret->{id_str} = $ot_registers->{$id}->{name};
+ $ret->{units} = $ot_registers->{$id}->{units};
+ $format = $ot_registers->{$id}->{format};
+ $bits = $ot_registers->{$id}->{names};
+ }
+
+ $ret->{format} = join( ',', @$format );
+ $ret->{raw_hex} = substr( $string, 4, 4 );
+ $ret->{raw} = $data;
+
+ $ret->{values} = [];
+ for my $f (@$format) {
+
+ if ( $f eq "u16" ) {
+ push @{ $ret->{values} }, unpack( 'S>', $data );
+ $data = substr( $data, 2 );
+ }
+ elsif ( $f eq 's16' ) {
+ push @{ $ret->{values} }, unpack( 's>', $data );
+ $data = substr( $data, 2 );
+ }
+ elsif ( $f eq 'u8' ) {
+ push @{ $ret->{values} }, unpack( 'C', $data );
+ $data = substr( $data, 1 );
+ }
+ elsif ( $f eq 's8' ) {
+ push @{ $ret->{values} }, unpack( 'c', $data );
+ $data = substr( $data, 1 );
+ }
+ elsif ( $f eq 'nu' ) {
+ $data = substr( $data, 1 );
+ }
+ elsif ( $f eq 'flag16' ) {
+ push @{ $ret->{values} },
+ ( unpack( "(A)*", unpack( "B16", $data ) ) );
+ $data = substr( $data, 2 );
+ }
+ elsif ( $f eq 'flag8' ) {
+ push @{ $ret->{values} },
+ ( unpack( "(A)*", unpack( "B8", $data ) ) );
+ $data = substr( $data, 1 );
+ }
+ elsif ( $f eq 'f8.8' ) {
+ push @{ $ret->{values} }, unpack( 's>', $data ) / 256.0;
+ }
+
+ }
+
+ $dataref->{ $id + $offset } = $ret;
+
+ if ( defined $bits ) {
+ my @vs = ( @{ $ret->{values} } );
+ my $bit = scalar( @{ $ret->{values} } );
+
+ for my $label (@$bits) {
+ $bit--;
+ my $v = shift(@vs);
+ next unless length($label) > 0;
+ my $key = sprintf( "%d.%d", $id + $offset, $bit );
+ $ret = {};
+
+ $ret->{id} = sprintf( "%d.%d", $id, $bit );
+ $ret->{fake_id} = $key;
+ $ret->{type} = $type;
+ $ret->{type_str} = $ot_types->{$type};
+ $ret->{id_str} = $label;
+ $ret->{values} = [$v];
+
+ $dataref->{$key} = $ret;
+ }
+ }
+
+ }
+
+ sub get_units($) {
+ my $id = shift;
+
+ if ( $id =~ /^\d+$/ ) {
+ return '' unless exists $ot_registers->{$id};
+ return '' unless exists $ot_registers->{$id}->{units};
+ return $ot_registers->{$id}->{units};
+ }
+ elsif ( $id =~ /^(\d+)\.(\d+)$/ ) {
+ my $base_id = $1;
+ my $bit = $2;
+
+ return '' unless exists $ot_registers->{$base_id};
+ return '' unless exists $ot_registers->{$base_id}->{unitss};
+
+ $bit =
+ ( scalar( @{ $ot_registers->{$base_id}->{unitss} } ) - 1 ) - $bit;
+ return $ot_registers->{$base_id}->{unitss}->[$bit];
+ }
+ else {
+ return '';
+ }
+ }
+
+ sub get_name($) {
+ my $id = shift;
+
+ if ( $id =~ /^\d+$/ ) {
+ return "Unknown $id" unless exists $ot_registers->{$id};
+ return $ot_registers->{$id}->{name};
+ }
+ elsif ( $id =~ /^(\d+)\.(\d+)$/ ) {
+ my $base_id = $1;
+ my $bit = $2;
+
+ return "Unknown $id" unless exists $ot_registers->{$base_id};
+ return "Unknown $id"
+ unless exists $ot_registers->{$base_id}->{names};
+
+ $bit =
+ ( scalar( @{ $ot_registers->{$base_id}->{names} } ) - 1 ) - $bit;
+ return $ot_registers->{$base_id}->{names}->[$bit];
+ }
+ else {
+ return undef;
+ }
+ }
+
+ my $report = [
+ '0.0', '0.1', '0.2', '0.3', '0.6', '0.8', '0.9', '17', '18', '25',
+ '26', '28', '57', '80', '257', '270', '272', '280', '312'
+ ];
+
+ sub load($) {
+ my $host = shift;
+ my $path = "/var/run/boiler_" . $host;
+
+ return undef unless -d $path;
+
+ my $wanted = { map { my $q = $_; $q =~ s/\..*$//; $q => 1 } @$report };
+
+ my $data = {};
+
+ for my $t ( keys %$wanted ) {
+ if ( $t < 0x100 ) {
+ my $fn = sprintf( "%s/B4x%02X", $path, $t );
+ my $guts = fetch_file($fn);
+ decode( $data, substr( $guts, 1 ), 0x0 ) if defined $guts;
+ }
+ else {
+ my $fn = sprintf( "%s/T9x%02X", $path, $t - 0x100 );
+ my $guts = fetch_file($fn);
+ decode( $data, substr( $guts, 1 ), 0x100 ) if defined $guts;
+ }
+ }
+
+ return $data;
+ }
+
+ 1;
+}
+
+my $mqtt_host = "10.32.139.1";
+my $mqtt_data = {};
+my $outside_temp = "";
+
+sub fmt($) {
+ my $v = shift;
+ return sprintf( "%.1f", $v );
+}
+
+sub fmt2($) {
+ my $v = shift;
+ return sprintf( "%.2f", $v );
+}
+
+sub mqtt_msg($$) {
+ my ( $topic, $message ) = @_;
+
+ $outside_temp = $message if $topic eq 'tele/weather/tempc';
+
+ return unless $topic =~ /^[^\/]+\/[^\/]+_radiator/;
+ return unless $topic =~ /^[^\/]+\/[^\/]+_radiator/;
+ return unless $topic =~ /^[^\/]+\/([^\/]+)\/(.+)$/;
+
+ $mqtt_data->{$1}->{$2} = $message;
+}
+
+sub do_radiators() {
+ my $mqtt = Net::MQTT::Simple->new($mqtt_host);
+
+ $mqtt->subscribe( 'stat/+/+', \&mqtt_msg );
+ $mqtt->subscribe( 'tele/+/SENSOR', \&mqtt_msg );
+ $mqtt->subscribe( 'tele/weather/tempc', \&mqtt_msg );
+
+ my $then = time;
+ $mqtt->tick(1) while ( time - $then ) < 3;
+
+ $mqtt->disconnect();
+
+ for my $r ( keys(%$mqtt_data) ) {
+ if ( exists $mqtt_data->{$r}->{SENSOR} ) {
+ my $pj = JSON::Parse::parse_json( $mqtt_data->{$r}->{SENSOR} );
+
+ if ( exists $pj->{'SI7021'} ) {
+ $mqtt_data->{$r}->{'TEMPERATURE'} =
+ $pj->{'SI7021'}->{'Temperature'}
+ if exists $pj->{'SI7021'}->{'Temperature'};
+ $mqtt_data->{$r}->{'HUMIDITY'} = $pj->{'SI7021'}->{'Humidity'}
+ if exists $pj->{'SI7021'}->{'Humidity'};
+ }
+ }
+
+ if ( exists $mqtt_data->{$r}->{POWER} ) {
+ if ( $mqtt_data->{$r}->{POWER} =~ /off/i ) {
+ $mqtt_data->{$r}->{POWER} = 0;
+ }
+ else {
+ $mqtt_data->{$r}->{POWER} = 1;
+ }
+ }
+
+ if ( ( $mqtt_data->{$r}->{POWER} == 0 )
+ && ( $mqtt_data->{$r}->{OPEN} == 0 ) )
+ {
+ $mqtt_data->{$r}->{state} = "closed";
+ $mqtt_data->{$r}->{state_colour} = "#c0c0ff";
+ }
+ elsif (( $mqtt_data->{$r}->{POWER} == 1 )
+ && ( $mqtt_data->{$r}->{OPEN} == 0 ) )
+ {
+ $mqtt_data->{$r}->{state} = "opening";
+ $mqtt_data->{$r}->{state_colour} = "#ffe0e0";
+ }
+ elsif (( $mqtt_data->{$r}->{POWER} == 1 )
+ && ( $mqtt_data->{$r}->{OPEN} == 1 ) )
+ {
+ $mqtt_data->{$r}->{state} = "open";
+ $mqtt_data->{$r}->{state_colour} = "#ffc0c0";
+ }
+ elsif (( $mqtt_data->{$r}->{POWER} == 0 )
+ && ( $mqtt_data->{$r}->{OPEN} == 1 ) )
+ {
+ $mqtt_data->{$r}->{state} = "closing";
+ $mqtt_data->{$r}->{state_colour} = "#e0e0ff";
+ }
+ else {
+ $mqtt_data->{$r}->{state} = "unknown";
+ $mqtt_data->{$r}->{state_colour} = "#ffffff";
+ }
+
+ if ( $mqtt_data->{$r}->{TEMPERATURE} < $mqtt_data->{$r}->{var1} ) {
+ $mqtt_data->{$r}->{temp_colour} = "#c0c0ff";
+ }
+ elsif ( $mqtt_data->{$r}->{TEMPERATURE} > $mqtt_data->{$r}->{var2} ) {
+ $mqtt_data->{$r}->{temp_colour} = "#ffc0c0";
+ }
+ else {
+ $mqtt_data->{$r}->{temp_colour} = "#ffffff";
+ }
+
+ for my $t (qw(var1 var2 TEMPERATURE HUMIDITY DELTA)) {
+ next unless exists $mqtt_data->{$r}->{$t};
+
+ $mqtt_data->{$r}->{$t} = fmt( $mqtt_data->{$r}->{$t} );
+ }
+
+ }
+
+ print "<table>\n";
+ print
+"<tr><th align=left><a href='http://munin.backdown.james.local/prometheus.james.local/prometheus.prometheus.james.local/index.html'>Radiators</a></th><th>Low</th><th>Temp</th><th>High</th><th>Humid</th><th>Delta</th><th>Valve</th><th>Sensor</th><th colspan=8 align=left>Set target</th></tr>\n";
+ for my $r ( sort( keys(%$mqtt_data) ) ) {
+ my $rd = $mqtt_data->{$r};
+
+ print "<tr>";
+ print
+"<td><a href='http://munin.backdown.james.local/prometheus.james.local/prometheus.prometheus.james.local/radiator_"
+ . $r
+ . "_temp.html'>", $r, "</a></td>";
+ print "<td>", $rd->{var1}, "</td>";
+ print "<td style='background-color: " . ${rd}->{temp_colour} . "' >",
+ $rd->{TEMPERATURE}, "</td>";
+ print "<td>", $rd->{var2}, "</td>";
+ print "<td>", $rd->{HUMIDITY}, "</td>";
+ print "<td>", $rd->{DELTA}, "</td>";
+ print "<td style='background-color: " . ${rd}->{state_colour} . "' >",
+ $rd->{state}, "</td>";
+
+ if ( exists $rd->{failed_reads} and ( $rd->{failed_reads} > 5 ) ) {
+ print "<td><font color='red'>Failed</font></td>";
+ }
+ else {
+ print "<td>Ok</td>";
+ }
+
+ for my $t (qw(10 15 18 19 20 21 22 23 24 25)) {
+ my $s = "";
+ $s = "color: red" if $t == $rd->{var1};
+ print "<td>";
+ print submit( -name => $r, -value => $t, -style => $s );
+ print "</td>";
+ }
+
+ print "</tr>\n";
+ }
+
+ print "</table>\n";
+}
+
+sub do_boiler($) {
+ my $outside_temp = shift;
+
+ my $boiler = Boiler::load("boiler-monster.prometheus.james.local");
+
+ return unless defined $boiler;
+
+ my $url =
+'http://munin.backdown.james.local/prometheus.james.local/boilermonster.prometheus.james.local/';
+ print "<table>\n";
+ print "<tr><th colspan=2 align=left><a href='" . $url
+ . "index.html'>Boiler</a></th></tr>\n";
+ my $pump = $boiler->{0.1}->{values}->[0];
+ my $pump_colour = '#c0c0ff';
+ $pump_colour = '#ffc0c0' if $pump == 1;
+ print "<tr><td><a href='"
+ . $url
+ . "boiler_ch_active_boilermonster.html'>CH Pump running:</a></td><td style='background-color: "
+ . $pump_colour
+ . "' >", $pump,
+ "</td></tr>\n";
+ print "<tr><td><a href='"
+ . $url
+ . "ch_temperatures_boilermonster.html'>Target water temp:</a></td><td>",
+ fmt( $boiler->{257}->{values}->[0] ),
+ "</td></tr>\n";
+ print "<tr><td><a href='"
+ . $url
+ . "ch_temperatures_boilermonster.html'>Current water temp:</a></td><td>",
+ fmt( $boiler->{25}->{values}->[0] ),
+ "</td></tr>\n";
+ print "<tr><td><a href='"
+ . $url
+ . "ch_temperatures_boilermonster.html'>Return water temp:</a></td><td>",
+ fmt( $boiler->{28}->{values}->[0] ),
+ "</td></tr>\n";
+ print "<tr><td><a href='"
+ . $url
+ . "modulation_boilermonster.html'>Modulation level:</a></td><td>",
+ fmt( $boiler->{17}->{values}->[0] ),
+ "</td></tr>\n";
+ print "<tr><td><a href='"
+ . $url
+ . "pressures_boilermonster.html'>CH Pressure:</a></td><td>",
+ fmt2( $boiler->{18}->{values}->[0] ),
+ "</td></tr>\n";
+ print "<tr><td><a href='"
+ . $url
+ . "boiler_fault_boilermonster.html'>Fault:</a></td><td>",
+ $boiler->{0.0}->{values}->[0],
+ "</td></tr>";
+ print
+"<tr><td><a href='http://weather.ourano.org/daytempdew.png'>Outside temp:</a></td><td>",
+ sprintf( "%.1f", $outside_temp ), "</td></tr>\n";
+ print "</table>\n";
+}
+
+my $css = <<'EOF';
+tr:nth-child(odd) td {
+ background-color: #ffffff
+}
+tr:nth-child(even) td {
+ background-color: #c0ffc0
+}
+EOF
+
+my $all_params = Vars();
+
+my $here = $ENV{SCRIPT_NAME};
+
+print header(
+ -type => 'text/html',
+ -charset => 'utf-8',
+ -refresh => '30; url=' . $here
+);
+
+print start_html(
+ -title => "heating",
+ -head => "<style>\n" . $css . "\n</style>"
+);
+
+print start_form();
+do_radiators();
+print end_form();
+
+print "<br/> <a href='" . $here . "'>Refresh</a></br>\n";
+
+if ( scalar( keys %$all_params ) > 0 ) {
+ print "<h2>Updates</h2>\n";
+ for my $param ( keys %$all_params ) {
+
+ my $mqtt = Net::MQTT::Simple->new($mqtt_host);
+ my $slop = 1;
+
+ $slop = 2 if $param =~ /bathroom/;
+
+ $mqtt->publish( "cmnd/" . $param . "/var1", $all_params->{$param} );
+ $mqtt->publish( "cmnd/" . $param . "/var2",
+ $all_params->{$param} + $slop );
+ $mqtt->tick(1);
+ $mqtt->disconnect();
+
+ print "Set $param to ", fmt( $all_params->{$param} ), "-",
+ fmt( $all_params->{$param} + $slop ), "<br/>";
+
+ }
+ print "<br\>";
+}
+
+print "<br\>";
+do_boiler($outside_temp);
+print end_html();
diff --git a/heating-cgi/run_boiler_rx b/heating-cgi/run_boiler_rx
new file mode 100755
index 0000000..c607ab3
--- /dev/null
+++ b/heating-cgi/run_boiler_rx
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+while true; do
+/usr/local/sbin/boiler_rx "$@"
+sleep 30
+done
diff --git a/munin/.gitignore b/munin/.gitignore
deleted file mode 100644
index 751553b..0000000
--- a/munin/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-*.bak
diff --git a/munin/boiler b/munin/boiler
deleted file mode 100755
index 199893c..0000000
--- a/munin/boiler
+++ /dev/null
@@ -1,552 +0,0 @@
-#!/usr/bin/env perl
-
-use Net::Telnet;
-use Data::Dumper;
-
-#0 Status => 0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-#17 Relative modulation level => 0
-#18 CH water pressure => 1.14453125
-#25 Boiler water temperature => 53
-#26 DHW temperature => 49
-#28 Return water temperature => 48.375
-#57 Max CH water setpoint => 80
-#80 Supply inlet temperature => 21.25
-#256 Status => 0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0
-#257 Control setpoint => 7
-#270 Maximum relative modulation level => 100
-#272 Room setpoint => 18
-#280 Room temperature => 26.7265625
-#312 DHW setpoint => 60
-
-my $ot_registers = {
- 0 => {
- format => ["flag16"],
- name => "Status",
- names => [
- '', '',
- '', '',
- '', '',
- 'DHW enabled', 'CH enabled',
- '', 'diagnostic indication',
- '', '',
- 'Flame', 'DHW active',
- 'CH active', 'fault'
- ],
- warnings => [
- undef, undef, undef, undef, undef, undef, undef, undef,
- undef, undef, undef, undef, undef, undef, undef, '1'
- ]
- },
- 1 => { format => ["f8.8"], name => "Control setpoint", units => 'C' },
- 2 => { format => [ "flag8", "u8" ], name => "Master configuration" },
- 3 => { format => [ "flag8", "u8" ], name => "Slave configuration" },
- 4 => { format => [ "u8", "u8" ], name => "Remote command" },
- 5 => { format => [ "flag8", "u8" ], name => "Application-specific flags" },
- 6 => { format => [ "flag8", "flag8" ], name => "Remote parameter flags" },
- 7 => { format => ["f8.8"], name => "Cooling control signal" },
- 8 => { format => ["f8.8"], name => "Control setpoint 2" },
- 9 => { format => ["f8.8"], name => "Remote override room setpoint" },
- 10 => { format => [ "u8", "nu" ], name => "Number of TSPs" },
- 11 => { format => [ "u8", "u8" ], name => "TSP setting" },
- 12 => { format => [ "u8", "nu" ], name => "Size of fault buffer" },
- 13 => { format => [ "u8", "u8" ], name => "Fault buffer entry" },
- 14 => {
- format => ["f8.8"],
- name => "Maximum relative modulation level",
- units => '%'
- },
- 15 => {
- format => [ "u8", "u8" ],
- name => "Boiler capacity and modulation limits"
- },
- 16 => { format => ["f8.8"], name => "Room setpoint", units => 'C' },
- 17 =>
- { format => ["f8.8"], name => "Relative modulation level", units => '%' },
- 18 => {
- format => ["f8.8"],
- name => "CH water pressure",
- units => 'bar',
- warning => '0.9:2.6',
- critical => '0.8:2.8'
- },
- 19 => { format => ["f8.8"], name => "DHW flow rate" },
- 20 => { format => ["time"], name => "Day of week and time of day" },
- 21 => { format => ["date"], name => "Date" },
- 22 => { format => ["u16"], name => "Year" },
- 23 => { format => ["f8.8"], name => "Room Setpoint CH2" },
- 24 => { format => ["f8.8"], name => "Room temperature", units => 'C' },
- 25 =>
- { format => ["f8.8"], name => "Boiler water temperature", units => 'C' },
- 26 => { format => ["f8.8"], name => "DHW temperature", units => 'C' },
- 27 => { format => ["f8.8"], name => "Outside temperature" },
- 28 =>
- { format => ["f8.8"], name => "Return water temperature", units => 'C' },
- 29 => { format => ["f8.8"], name => "Solar storage temperature" },
- 30 => { format => ["f8.8"], name => "Solar collector temperature" },
- 31 => { format => ["f8.8"], name => "Flow temperature CH2" },
- 32 => { format => ["f8.8"], name => "DHW2 temperature" },
- 33 => { format => ["s16"], name => "Exhaust temperature" },
- 34 => { format => ["f8.8"], name => "Boiler heat exchanger temperature" },
- 35 => { format => [ "u8", "u8" ], name => "Boiler fan speed and setpoint" },
- 48 => { format => [ "s8", "s8" ], name => "DHW setpoint boundaries" },
- 49 => { format => [ "s8", "s8" ], name => "Max CH setpoint boundaries" },
- 50 =>
- { format => [ "s8", "s8" ], name => "OTC heat curve ratio boundaries" },
- 51 => { format => [ "s8", "s8" ], name => "Remote parameter 4 boundaries" },
- 52 => { format => [ "s8", "s8" ], name => "Remote parameter 5 boundaries" },
- 53 => { format => [ "s8", "s8" ], name => "Remote parameter 6 boundaries" },
- 54 => { format => [ "s8", "s8" ], name => "Remote parameter 7 boundaries" },
- 55 => { format => [ "s8", "s8" ], name => "Remote parameter 8 boundaries" },
- 56 => { format => ["f8.8"], name => "DHW setpoint", units => 'C' },
- 57 => { format => ["f8.8"], name => "Max CH water setpoint", units => 'C' },
- 58 => { format => ["f8.8"], name => "OTC heat curve ratio" },
- 59 => { format => ["f8.8"], name => "Remote parameter 4" },
- 60 => { format => ["f8.8"], name => "Remote parameter 5" },
- 61 => { format => ["f8.8"], name => "Remote parameter 6" },
- 62 => { format => ["f8.8"], name => "Remote parameter 7" },
- 63 => { format => ["f8.8"], name => "Remote parameter 8" },
- 70 => { format => [ "flag8", "flag8" ], name => "Status V/H" },
- 71 => { format => [ "nu", "u8" ], name => "Control setpoint V/H" },
- 72 => { format => [ "flag8", "u8" ], name => "Fault flags/code V/H" },
- 73 => { format => ["u16"], name => "OEM diagnostic code V/H" },
- 74 => { format => [ "flag8", "u8" ], name => "Configuration/memberid V/H" },
- 75 => { format => ["f8.8"], name => "OpenTherm version V/H" },
- 76 => { format => [ "u8", "u8" ], name => "Product version V/H" },
- 77 => { format => [ "nu", "u8" ], name => "Relative ventilation" },
- 78 => { format => [ "u8", "u8" ], name => "Relative humidity exhaust air" },
- 79 => { format => ["u16"], name => "CO2 level exhaust air" },
- 80 =>
- { format => ["f8.8"], name => "Supply inlet temperature", units => 'C' },
- 81 => { format => ["f8.8"], name => "Supply outlet temperature" },
- 82 => { format => ["f8.8"], name => "Exhaust inlet temperature" },
- 83 => { format => ["f8.8"], name => "Exhaust outlet temperature" },
- 84 => { format => ["u16"], name => "Exhaust fan speed" },
- 85 => { format => ["u16"], name => "Inlet fan speed" },
- 86 => {
- format => [ "flag8", "flag8" ],
- name => "Remote parameter settings V/H"
- },
- 87 => { format => [ "u8", "nu" ], name => "Nominal ventilation value" },
- 88 => { format => [ "u8", "nu" ], name => "Number of TSPs V/H" },
- 89 => { format => [ "u8", "u8" ], name => "TSP setting V/H" },
- 90 => { format => [ "u8", "nu" ], name => "Size of fault buffer V/H" },
- 91 => { format => [ "u8", "u8" ], name => "Fault buffer entry V/H" },
- 100 => { format => [ "nu", "flag8" ], name => "Remote override function" },
- 101 => {
- format => [ "flag8", "flag8" ],
- name => "Solar storage mode and status"
- },
- 102 => { format => [ "flag8", "u8" ], name => "Solar storage fault flags" },
- 103 =>
- { format => [ "flag8", "u8" ], name => "Solar storage config/memberid" },
- 104 =>
- { format => [ "u8", "u8" ], name => "Solar storage product version" },
- 105 => { format => [ "u8", "nu" ], name => "Number of TSPs solar storage" },
- 106 => { format => [ "u8", "u8" ], name => "TSP setting solar storage" },
- 107 => {
- format => [ "u8", "u8" ],
- name => "Size of fault buffer solar storage"
- },
- 108 =>
- { format => [ "u8", "u8" ], name => "Fault buffer entry solar storage" },
- 113 => { format => ["u16"], name => "Unsuccessful burner starts" },
- 114 => { format => ["u16"], name => "Flame signal too low count" },
- 115 => { format => ["u16"], name => "OEM diagnostic code" },
- 116 => { format => ["u16"], name => "Burner starts" },
- 117 => { format => ["u16"], name => "CH pump starts" },
- 118 => { format => ["u16"], name => "DHW pump/valve starts" },
- 119 => { format => ["u16"], name => "DHW burner starts" },
- 120 => { format => ["u16"], name => "Burner operation hours" },
- 121 => { format => ["u16"], name => "CH pump operation hours" },
- 122 => { format => ["u16"], name => "DHW pump/valve operation hours" },
- 123 => { format => ["u16"], name => "DHW burner operation hours" },
- 124 => { format => ["f8.8"], name => "OpenTherm version Master" },
- 125 => { format => ["f8.8"], name => "OpenTherm version Slave" },
- 126 => { format => [ "u8", "u8" ], name => "Master product version" },
- 127 => { format => [ "u8", "u8" ], name => "Slave product version" },
-};
-
-my $ot_types = {
- 0 => "read data",
- 1 => "write data",
- 2 => "invalidate data",
- 3 => "unknown 0x3",
- 4 => "read ack",
- 5 => "write ack",
- 6 => "data invalid",
- 7 => "unknown 0x7"
-};
-
-sub decode($$$) {
- my ( $dataref, $string, $offset ) = @_;
- my $b0 = hex( substr( $string, 0, 2 ) );
-
- my $type = ( $b0 >> 4 ) & 7;
- my $id = hex( substr( $string, 2, 2 ) );
-
- my $data = pack( 'H4', substr( $string, 4, 4 ) );
- my $format = ["u16"];
-
- my $ret = {
- id => $id,
- fake_id => $id + $offset,
- type => $type,
- type_str => $ot_types->{$type},
- };
-
- my $bits = undef;
-
- if ( exists $ot_registers->{$id} ) {
- $ret->{id_str} = $ot_registers->{$id}->{name};
- $ret->{units} = $ot_registers->{$id}->{units};
- $format = $ot_registers->{$id}->{format};
- $bits = $ot_registers->{$id}->{names};
- }
-
- $ret->{format} = join( ',', @$format );
- $ret->{raw_hex} = substr( $string, 4, 4 );
- $ret->{raw} = $data;
-
- $ret->{values} = [];
- for my $f (@$format) {
-
- if ( $f eq "u16" ) {
- push @{ $ret->{values} }, unpack( 'S>', $data );
- $data = substr( $data, 2 );
- }
- elsif ( $f eq 's16' ) {
- push @{ $ret->{values} }, unpack( 's>', $data );
- $data = substr( $data, 2 );
- }
- elsif ( $f eq 'u8' ) {
- push @{ $ret->{values} }, unpack( 'C', $data );
- $data = substr( $data, 1 );
- }
- elsif ( $f eq 's8' ) {
- push @{ $ret->{values} }, unpack( 'c', $data );
- $data = substr( $data, 1 );
- }
- elsif ( $f eq 'nu' ) {
- $data = substr( $data, 1 );
- }
- elsif ( $f eq 'flag16' ) {
- push @{ $ret->{values} },
- ( unpack( "(A)*", unpack( "B16", $data ) ) );
- $data = substr( $data, 2 );
- }
- elsif ( $f eq 'flag8' ) {
- push @{ $ret->{values} },
- ( unpack( "(A)*", unpack( "B8", $data ) ) );
- $data = substr( $data, 1 );
- }
- elsif ( $f eq 'f8.8' ) {
- push @{ $ret->{values} }, unpack( 's>', $data ) / 256.0;
- }
-
- }
-
- $dataref->{ $id + $offset } = $ret;
-
- if ( defined $bits ) {
- my @vs = ( @{ $ret->{values} } );
- my $bit = scalar( @{ $ret->{values} } );
-
- for my $label (@$bits) {
- $bit--;
- my $v = shift(@vs);
- next unless length($label) > 0;
- my $key = sprintf( "%d.%d", $id + $offset, $bit );
- $ret = {};
-
- $ret->{id} = sprintf( "%d.%d", $id, $bit );
- $ret->{fake_id} = $key;
- $ret->{type} = $type;
- $ret->{type_str} = $ot_types->{$type};
- $ret->{id_str} = $label;
- $ret->{values} = [$v];
-
- $dataref->{$key} = $ret;
- }
- }
-
- return $ret;
-}
-
-sub thermostat($$) {
- my ( $string, $data ) = @_;
-
- return decode( $data, $string, 0x100 );
-
-}
-
-sub boiler($$) {
- my ( $string, $data ) = @_;
-
- return decode( $data, $string, 0x0 );
-
-}
-
-sub get_units($) {
- my $id = shift;
-
- if ( $id =~ /^\d+$/ ) {
- return '' unless exists $ot_registers->{$id};
- return '' unless exists $ot_registers->{$id}->{units};
- return $ot_registers->{$id}->{units};
- }
- elsif ( $id =~ /^(\d+)\.(\d+)$/ ) {
- my $base_id = $1;
- my $bit = $2;
-
- return '' unless exists $ot_registers->{$base_id};
- return '' unless exists $ot_registers->{$base_id}->{unitss};
-
- $bit =
- ( scalar( @{ $ot_registers->{$base_id}->{unitss} } ) - 1 ) - $bit;
- return $ot_registers->{$base_id}->{unitss}->[$bit];
- }
- else {
- return '';
- }
-}
-
-sub get_name($) {
- my $id = shift;
-
- if ( $id =~ /^\d+$/ ) {
- return "Unknown $id" unless exists $ot_registers->{$id};
- return $ot_registers->{$id}->{name};
- }
- elsif ( $id =~ /^(\d+)\.(\d+)$/ ) {
- my $base_id = $1;
- my $bit = $2;
-
- return "Unknown $id" unless exists $ot_registers->{$base_id};
- return "Unknown $id" unless exists $ot_registers->{$base_id}->{names};
-
- $bit = ( scalar( @{ $ot_registers->{$base_id}->{names} } ) - 1 ) - $bit;
- return $ot_registers->{$base_id}->{names}->[$bit];
- }
- else {
- return undef;
- }
-}
-
-sub get_warning($) {
- my $id = shift;
-
- if ( $id =~ /^\d+$/ ) {
- return "Unknown $id" unless exists $ot_registers->{$id};
- return $ot_registers->{$id}->{warning};
- }
- elsif ( $id =~ /^(\d+)\.(\d+)$/ ) {
- my $base_id = $1;
- my $bit = $2;
-
- return "Unknown $id" unless exists $ot_registers->{$base_id};
- return "Unknown $id" unless exists $ot_registers->{$base_id}->{names};
-
- $bit =
- ( scalar( @{ $ot_registers->{$base_id}->{warnings} } ) - 1 ) - $bit;
- return $ot_registers->{$base_id}->{warnings}->[$bit];
- }
- else {
- return undef;
- }
-}
-
-sub get_critical($) {
- my $id = shift;
-
- if ( $id =~ /^\d+$/ ) {
- return "Unknown $id" unless exists $ot_registers->{$id};
- return $ot_registers->{$id}->{crtitical};
- }
- elsif ( $id =~ /^(\d+)\.(\d+)$/ ) {
- my $base_id = $1;
- my $bit = $2;
-
- return "Unknown $id" unless exists $ot_registers->{$base_id};
- return "Unknown $id" unless exists $ot_registers->{$base_id}->{names};
-
- $bit =
- ( scalar( @{ $ot_registers->{$base_id}->{crtiticals} } ) - 1 ) - $bit;
- return $ot_registers->{$base_id}->{crtiticals}->[$bit];
- }
- else {
- return undef;
- }
-}
-
-sub maul($) {
- my $n = shift;
-
- $n =~ s/\s/_/g;
- return "boiler_" . lc($n);
-}
-
-my $report = [
- '0.0', '0.1', '0.2', '0.3', '0.6', '0.8', '0.9', '17', '18', '25',
- '26', '28', '57', '80', '257', '270', '272', '280', '312'
-];
-
-sub do_thing($) {
- my $t = shift;
-
- my $name = get_name($t);
- my $maul = maul($name);
- my $warning = get_warning($t);
- my $critical = get_critical($t);
-
- print "$maul.draw LINE2\n";
- print "$maul.type GAUGE\n";
- print "$maul.label $name\n";
-
- print "$maul.warning ", $warning, "\n" if $warning;
- print "$maul.critical ", $critical, "\n" if $critical;
-}
-
-sub do_units($) {
- my $want_unit = shift;
-
- for my $t (@$report) {
- my $unit = get_units($t);
-
- next unless $want_unit eq $unit;
- do_thing($t);
- }
-}
-
-sub do_values_units($$) {
- my ( $data, $wanted_units ) = @_;
-
- for my $t (@$report) {
- next unless exists $data->{$t};
-
- my $units = get_units($t);
- next unless $units eq $wanted_units;
-
- my $name = get_name($t);
- my $maul = maul($name);
-
- print $maul, ".value ", $data->{$t}->{values}->[0], "\n";
- }
-}
-
-my $wanted = { map { my $q = $_; $q =~ s/\..*$//; $q => 1 } @$report };
-my $n_wanted = scalar( keys %$wanted );
-
-for my $t ( keys %$ot_registers ) {
- $ot_registers->{ $t + 0x100 } = $ot_registers->{$t};
-}
-
-my @bits = split( /_+/, $0 );
-
-exit 0 unless scalar(@bits) > 1;
-
-my $host = $bits[1];
-
-$host =~ s/-//g;
-
-my $short_host = $host;
-
-$short_host =~ s/\..*$//;
-
-if ( $ARGV[0] eq 'config' ) {
-
- print "host_name ", $host, "\n";
-
- print "multigraph temperatures_${short_host}\n";
- print "graph_title Temperatures\n";
- print "graph_category boiler\n";
- print "graph_vlabel centigrade\n";
-
- do_units('C');
-
- print "multigraph pressures_${short_host}\n";
- print "graph_title Pressures\n";
- print "graph_category boiler\n";
- print "graph_vlabel bar\n";
-
- do_units('bar');
-
- print "multigraph modulation_${short_host}\n";
- print "graph_title Modulation\n";
- print "graph_category boiler\n";
- print "graph_vlabel bar\n";
-
- do_units('%');
-
- for my $t (@$report) {
- next if length( get_units($t) ) > 0;
-
- my $name = get_name($t);
- my $maul = maul($name);
-
- print "multigraph ${maul}_${short_host}\n";
- print "graph_title $name\n";
- print "graph_category boiler\n";
-
- do_thing($t);
-
- }
-
- exit(0);
-}
-
-my $telnet = new Net::Telnet(
- Timeout => 10,
- Telnetmode => 0,
- Binmode => 1,
- Host => $bits[1],
- Port => 2001
-);
-
-#$telnet->open($bits[1]);
-
-my $data = {};
-my $then = time;
-my $timeout = 60;
-
-while ( ($n_wanted) and ( ( time - $then ) < $timeout ) ) {
- my $line = $telnet->getline( Timeout => 1, Errmode => 'return' );
- my $id = -1;
-
- chomp $line;
- chomp $line;
-
- if ( $line =~ /^T/ ) {
- $id = thermostat( substr( $line, 1, 8 ), $data );
- }
- elsif ( $line =~ /^B/ ) {
- $id = boiler( substr( $line, 1, 8 ), $data );
- }
-
- if ( exists $wanted->{$id} ) {
- $n_wanted--;
- delete $wanted->{$id};
- }
-
-}
-
-print "multigraph temperatures_${short_host}\n";
-do_values_units( $data, 'C' );
-print "multigraph pressures_${short_host}\n";
-do_values_units( $data, 'bar' );
-print "multigraph modulation_${short_host}\n";
-do_values_units( $data, '%' );
-
-for my $t (@$report) {
- next unless exists $data->{$t};
-
- my $units = get_units($t);
- next unless $units eq '';
-
- my $name = get_name($t);
- my $maul = maul($name);
-
- print "multigraph ${maul}_${short_host}\n";
- print $maul, ".value ", $data->{$t}->{values}->[0], "\n";
-}
-
-exit(0);
diff --git a/prometheus/.gitignore b/prometheus/.gitignore
new file mode 100644
index 0000000..2adf2fd
--- /dev/null
+++ b/prometheus/.gitignore
@@ -0,0 +1,3 @@
+stamp
+*.bak
+*.swp
diff --git a/prometheus/Makefile b/prometheus/Makefile
new file mode 100644
index 0000000..4c6b24f
--- /dev/null
+++ b/prometheus/Makefile
@@ -0,0 +1,11 @@
+STUFF=$(shell find usr home \! -type d -print)
+
+stamp: ${STUFF}
+ tar cf - ${STUFF} | ssh prometheus "cd / && tar xvpf -"
+ touch $@
+
+tidy:
+ perltidy -b ./home/httpd/html/weatherstation/updateweatherstation.pl
+
+clean:
+ /bin/rm -f ./home/httpd/html/weatherstation/updateweatherstation.pl.bak
diff --git a/prometheus/home/httpd/html/weatherstation/updateweatherstation.php b/prometheus/home/httpd/html/weatherstation/updateweatherstation.php
new file mode 120000
index 0000000..b629fc9
--- /dev/null
+++ b/prometheus/home/httpd/html/weatherstation/updateweatherstation.php
@@ -0,0 +1 @@
+updateweatherstation.pl \ No newline at end of file
diff --git a/prometheus/home/httpd/html/weatherstation/updateweatherstation.pl b/prometheus/home/httpd/html/weatherstation/updateweatherstation.pl
new file mode 100755
index 0000000..87b8893
--- /dev/null
+++ b/prometheus/home/httpd/html/weatherstation/updateweatherstation.pl
@@ -0,0 +1,83 @@
+#!/usr/bin/perl
+
+#use CGI qw(:standard);
+#use Sys::Syslog qw(:standard :macros);
+use Net::MQTT::Simple;
+use IO::Socket;
+
+#openlog("weather", "", "local0");
+
+my ( $sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst ) =
+ gmtime(time);
+
+my @mons = qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
+my @days = qw(Sun Mon Tue Wed Thr Fri Sat);
+
+my $dt = sprintf(
+ "%s, %02d %s %04d %02d:%02d:%02d GMT",
+ $days[$wday], $mday, $mons[$mon], 1900 + $year,
+ $hour, $min, $sec
+);
+
+print "Content-Type: text/plain\r\n";
+print "Date: ", $dt, "\n";
+print "\n";
+print "success\n";
+
+my $mqtt = Net::MQTT::Simple->new("10.32.139.1");
+
+for my $t ( split( /&/, $ENV{'QUERY_STRING'} ) ) {
+ $t =~ s/%20/ /g;
+
+ next unless $t =~ /^([^=]*)=([^=]*)$/;
+
+ my ( $wot, $value ) = ( $1, $2 );
+
+ if ( $wot =~ /f$/ ) {
+ $wot =~ s/f$/c/;
+ $value = sprintf( "%.1f", 5.0 * ( ( $value - 32.0 ) / 9.0 ) );
+ }
+ elsif ( $wot eq "baromin" ) {
+ $wot = "barommb";
+ $value = sprintf( "%.1f", $value * 33.8639 );
+ }
+ elsif ( $wot =~ /in$/ ) {
+ $wot =~ s/in$/mm/;
+ $value = sprintf( "%.1f", $value * 25.4 );
+ }
+
+ if ( $wot eq "baromin" ) {
+ $wot = "barommb";
+ $value = sprintf( "%.1f", $value * 33.8639 );
+ }
+
+ if ( $wot eq "tempc" ) {
+ $mqtt->retain( "tele/weather/" . $wot, $value );
+ }
+ else {
+ $mqtt->publish( "tele/weather/" . $wot, $value );
+ }
+
+ #syslog(LOG_WARNING,"fish: $wot => $value");
+}
+
+$mqtt->tick(0);
+$mqtt->disconnect;
+
+alarm(5);
+
+my $sock = IO::Socket::INET->new(
+ PeerAddr => "88.96.137.6",
+ PeerPort => 30956,
+ Proto => 'tcp',
+ Timeout => 5
+);
+
+exit 0 unless defined $sock;
+
+$sock->print( "GET /weatherstation/updateweatherstation.php?"
+ . $ENV{'QUERY_STRING'}
+ . " HTTP/1.0\r\n" );
+$sock->print("\r\n");
+$sock->shutdown( $socket, 1 );
+
diff --git a/prometheus/usr/local/bin/make_habitable b/prometheus/usr/local/bin/make_habitable
new file mode 100755
index 0000000..730d632
--- /dev/null
+++ b/prometheus/usr/local/bin/make_habitable
@@ -0,0 +1,11 @@
+#!/bin/sh
+set -x
+
+for i in 2fl_main_radiator 2fl_stair_radiator bathroom_radiator; do
+ /usr/local/bin/set_radiator_target $i 20
+done
+
+
+for i in hall_radiator kitchen_radiator laundry_radiator kstudy_radiator bedroom_radiator ; do
+ set_radiator_target $i 18
+done
diff --git a/prometheus/usr/local/bin/midnight_tidy b/prometheus/usr/local/bin/midnight_tidy
new file mode 100755
index 0000000..2e6686e
--- /dev/null
+++ b/prometheus/usr/local/bin/midnight_tidy
@@ -0,0 +1,15 @@
+#!/bin/sh
+set -x
+
+/usr/local/bin/vent close
+
+for i in 2fl_main_radiator 2fl_stair_radiator bathroom_radiator bedroom_radiator hall_radiator kitchen_radiator kstudy_radiator laundry_radiator spare_bedroom_radiator; do
+ /usr/local/bin/set_radiator_target $i 10
+done
+
+for i in kitchen_left/POWER kitchen_right/POWER bathroom/POWER1 bathroom/POWER2 front_door/POWER 2ndfl_3gang_switch/POWER1 2ndfl_3gang_switch/POWER2 2ndfl_3gang_switch/POWER3; do
+ mosquitto_pub -h 127.0.0.1 -t cmnd/$i -m 0
+ sleep 1
+done
+
+
diff --git a/prometheus/usr/local/bin/set_radiator_target b/prometheus/usr/local/bin/set_radiator_target
new file mode 100755
index 0000000..c52d22a
--- /dev/null
+++ b/prometheus/usr/local/bin/set_radiator_target
@@ -0,0 +1,47 @@
+#!/bin/sh
+
+R=$1
+T1=$2
+
+
+case "$R" in
+ bathroom_radiator)
+ T2=$( expr $T1 + 2 )
+ ;;
+ *)
+ T2=$( expr $T1 + 1 )
+ ;;
+esac
+
+H=127.0.0.1
+
+for tries in 1 2 3 4 5 6 7 8 9 10 11 12; do
+
+ V1=$( mosquitto_sub -h 10.32.139.1 -t stat/$R/var1 --retained-only -W 1 | sed -e 's/\..*//' )
+ V2=$( mosquitto_sub -h 10.32.139.1 -t stat/$R/var2 --retained-only -W 1 | sed -e 's/\..*//' )
+
+ if [ $T1 -ne $V1 ]; then
+ mosquitto_pub -h $H -t cmnd/$R/var1 -m $T1
+ sleep 1
+ fi
+
+ if [ $T2 -ne $V2 ]; then
+ mosquitto_pub -h $H -t cmnd/$R/var2 -m $T2
+ sleep 1
+ fi
+
+
+ if [ \( $T1 -eq $V1 \) -a \( $T2 -eq $V2 \) ]; then
+ echo Success
+ exit 0
+ fi
+
+ sleep 3
+
+done
+
+echo Failed
+
+
+
+
diff --git a/prometheus/usr/local/bin/vent b/prometheus/usr/local/bin/vent
new file mode 100755
index 0000000..28e5ad4
--- /dev/null
+++ b/prometheus/usr/local/bin/vent
@@ -0,0 +1,17 @@
+#!/bin/sh
+
+case "$1" in
+ open)
+ mosquitto_pub -h 10.32.135.1 -t cmnd/vent/POWER2 -m 1
+ sleep 20
+ mosquitto_pub -h 10.32.135.1 -t cmnd/vent/POWER2 -m 0
+ ;;
+ close)
+ mosquitto_pub -h 10.32.135.1 -t cmnd/vent/POWER1 -m 1
+ sleep 20
+ mosquitto_pub -h 10.32.135.1 -t cmnd/vent/POWER1 -m 0
+ ;;
+ *)
+ echo unknown command
+ ;;
+esac
diff --git a/radiator-plc/stm32/Makefile.rules b/radiator-plc/stm32/Makefile.rules
index a723e6f..cf67b1a 100644
--- a/radiator-plc/stm32/Makefile.rules
+++ b/radiator-plc/stm32/Makefile.rules
@@ -137,7 +137,7 @@ srec: $(BINARY).srec
list: $(BINARY).list
images: $(BINARY).images
-flash: $(BINARY).flash
+#flash: $(BINARY).flash
%.images: %.bin %.hex %.srec %.list %.map %.dfu
@#printf "*** $* images generated ***\n"
diff --git a/radiator-plc/stm32/app/Makefile b/radiator-plc/stm32/app/Makefile
index 8ef06f7..2ff2f51 100644
--- a/radiator-plc/stm32/app/Makefile
+++ b/radiator-plc/stm32/app/Makefile
@@ -20,7 +20,8 @@
CPROTO=cproto
PROG=radiator-plc
-D=radiator-plc0
+D=radiator-plc0 radiator-plc1
+#D=10.32.94.238
V=1
default: ${PROG}.elf
@@ -34,17 +35,19 @@ OBJS = ${CSRCS:%.c=%.o}
${OBJS}:
+
include ../Makefile.include
INCLUDES += -I..
+flash: ${PROG}.hex
+ for d in ${D}; do ssh $$d flash_stm32 < ${PROG}.hex; done
+
protos: ${CSRCS}
echo -n > prototypes.h
${CPROTO} $(INCLUDES) $(DEFINES) -e -v ${CSRCS} > prototypes.h.tmp
mv -f prototypes.h.tmp prototypes.h
-flash: ${PROG}.hex
- ssh ${D} flash_stm32 < ${PROG}.hex
local_flash: ${PROG}.hex
$(Q)$(OOCD) -f $(OOCD_INTERFACE) \
diff --git a/radiator-plc/stm32/app/control.c b/radiator-plc/stm32/app/control.c
deleted file mode 100644
index f3a35c9..0000000
--- a/radiator-plc/stm32/app/control.c
+++ /dev/null
@@ -1,114 +0,0 @@
-#include "project.h"
-
-
-static uint32_t target = 25000;
-static uint32_t value = ~0U;
-
-#define SHORT 200
-#define LONG 1200
-#define REPEAT 400
-
-#define HYST 75UL
-
-
-static void
-show_temp (char t, uint32_t v, int y)
-{
- char buf[16];
- uint32_t i, f;
-
- i = v / 1000;
- f = v / 100 - (i * 10);
-
- if (v == ~0U)
- {
- sprintf (buf, "%c:error ", t);
- }
- else
- {
- sprintf (buf, "%c:%2d.%1d%cC", t, (int) i, (int) f, 0xdf);
- }
-
- lcd_write (buf, 0, y);
-}
-
-void
-control_update (void)
-{
-
- show_temp ('P', value, 0);
- show_temp ('T', target, 1);
-
- if (value == ~0U)
- relay_off ();
- else if (value < (target - HYST))
- relay_on ();
- else if (value > (target + HYST))
- relay_off ();
-
-
-}
-
-void
-control_tick (void)
-{
- static int down, up, update;
-
- if (!gpio_get (GPIOB, GPIO4))
- down++;
- else
- down = 0;
-
- if (!gpio_get (GPIOB, GPIO6))
- up++;
- else
- up = 0;
-
- if ((down == SHORT) || (down == LONG))
- {
- if (target > 100)
- target -= 100;
- if (down == LONG)
- down = LONG - REPEAT;
- control_update ();
- }
-
- if ((up == SHORT) || (up == LONG))
- {
- if (target < 99900)
- target += 100;
- if (up == LONG)
- up = LONG - REPEAT;
- control_update ();
- }
-
- update++;
-
- if (update == 1000)
- {
- control_update ();
- update = 0;
- }
-}
-
-void
-control_report (uint32_t v)
-{
-
- value = v;
- control_update ();
-}
-
-void
-control_init (void)
-{
-
- gpio_set_mode (GPIOB, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN,
- GPIO4 | GPIO6);
- gpio_set_mode (GPIOB, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_PUSHPULL,
- GPIO5);
- gpio_set (GPIOB, GPIO4 | GPIO6);
- gpio_clear (GPIOB, GPIO5);
-
-
-}
diff --git a/radiator-plc/stm32/app/logic.c b/radiator-plc/stm32/app/logic.c
index 8cab7ca..a9f845c 100644
--- a/radiator-plc/stm32/app/logic.c
+++ b/radiator-plc/stm32/app/logic.c
@@ -7,7 +7,7 @@ static volatile int semaphore = 0;
-static const char *valve_name[N_VALVES] = {
+static const char *plc0_valve_names[N_VALVES] = {
"dd_radiator1",
"dd_radiator2",
"dd_radiator3",
@@ -18,16 +18,57 @@ static const char *valve_name[N_VALVES] = {
"plc0_radiator7",
};
-static Onewire_addr valve_owa[N_VALVES] = {
- {{0x28, 0xe3, 0x5d, 0x56, 0xb5, 0x01, 0x3c, 0x96}},
- {{0x28, 0x0d, 0xaf, 0x56, 0xb5, 0x01, 0x3c, 0x89}},
- {{0x28, 0x79, 0xb1, 0x56, 0xb5, 0x01, 0x3c, 0x13}},
- {{0x28, 0x99, 0x8f, 0x56, 0xb5, 0x01, 0x3c, 0x1c}},
- {{0x28, 0xb4, 0x98, 0x6e, 0x32, 0x20, 0x01, 0xc7}},
- {{0x28, 0x0d, 0x4d, 0x56, 0xb5, 0x01, 0x3c, 0xd0}},
- {{0x28, 0xb1, 0x71, 0x75, 0x32, 0x20, 0x01, 0xa9}},
+static const Onewire_addr plc0_valve_owas[N_VALVES] = {
+ {{0x28, 0x09, 0xb3, 0xc3, 0x0b, 0x00, 0x00, 0xf9}},
+ {{0x28, 0x89, 0xda, 0xc3, 0x0b, 0x00, 0x00, 0xf1}},
+ {{0x28, 0xdf, 0xb2, 0xc6, 0x0b, 0x00, 0x00, 0xc3}},
+ {{0x28, 0x42, 0x23, 0xc4, 0x0b, 0x00, 0x00, 0x45}},
+ {{0x28, 0xf9, 0xd6, 0xc6, 0x0b, 0x00, 0x00, 0xc9}},
+ {{0x28, 0x8d, 0x0a, 0xc6, 0x0b, 0x00, 0x00, 0xf2}},
+ {{0x28, 0x5f, 0xb7, 0xc6, 0x0b, 0x00, 0x00, 0xfb}},
+ {{0x28, 0x25, 0xe5, 0xc6, 0x0b, 0x00, 0x00, 0xba}},
+};
+
+
+
+
+
+
+
+
+
+
+
+
+static const char *plc1_valve_names[N_VALVES] = {
+ "2fl_main_radiator",
+ "2fl_stair_radiator",
+ "plc1_radiator2",
+ "plc1_radiator3",
+ "plc1_radiator4",
+ "plc1_radiator5",
+ "plc1_radiator6",
+ "plc1_radiator7",
+};
+
+
+static const Onewire_addr plc1_valve_owas[N_VALVES] = {
+ {{0x28, 0x03, 0x01, 0xc5, 0x0b, 0x00, 0x00, 0xf3}},
+ {{0x28, 0xea, 0x79, 0xc5, 0x0b, 0x00, 0x00, 0x1f}},
+ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
+ {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
{{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}},
};
+
+
+Onewire_addr owa_zero = {0};
+
+
+static const char **valve_name;
+static const Onewire_addr *valve_owa;
volatile int temp[N_VALVES];
#define BAD_TEMP 0x7fffffff
@@ -56,80 +97,98 @@ int high_limit[N_VALVES] = {
};
int valve_state[N_VALVES];
+int failed_reads[N_VALVES];
+
+
+
+static float scale (int i)
+{
+ float ret;
+ ret = i;
+ return ret / 1000.;
+}
void mqtt_dispatch (char *type, char *who, char *what, char *msg)
{
unsigned i;
+ static char msg_buf[80];
if (strcmp (type, "cmnd")) return;
for (i = 0; i < N_VALVES; ++i) {
if (!strcmp (who, valve_name[i])) {
- if (!strcmp (what, "var1"))
+ if (!strcmp (what, "var1")) {
low_limit[i] = atoi (msg) * 1000;
+ usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/var1 %.2f\r\n", valve_name[i], scale (low_limit[i])));
+ }
- if (!strcmp (what, "var2"))
+ if (!strcmp (what, "var2")) {
high_limit[i] = atoi (msg) * 1000;
- }
- }
-}
-
+ usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/var2 %.2f\r\n", valve_name[i], scale (high_limit[i])));
+ }
-static float scale(int i)
-{
-float ret;
-ret=i;
-return ret/1000.;
+ }
+ }
}
void logic_tick (void)
{
static unsigned i;
static int read_failed = 1;
+ static int not_present = 1;
static char msg_buf[80];
int t;
int o;
int v;
+ if (!not_present) {
+ if (!read_failed)
+ read_failed = ds18b20_read (&valve_owa[i], &t);
+ if (read_failed) {
+ temp[i] = BAD_TEMP;
+ valve_state[i] = 0;
+ failed_reads[i]++;
+ } else {
+ temp[i] = t;
+ failed_reads[i] = 0;
- if (!read_failed)
- read_failed = ds18b20_read (&valve_owa[i], &t);
+ if (t < low_limit[i])
+ valve_state[i] = 1;
- if (read_failed) {
- temp[i] = BAD_TEMP;
- valve_state[i] = 0;
- } else {
- temp[i] = t;
+ if (t >= high_limit[i])
+ valve_state[i] = 0;
+ }
- if (t < low_limit[i])
- valve_state[i] = 1;
+ o = valve_state[i];
- if (t >= high_limit[i])
- valve_state[i] = 0;
- }
+ output_write (i, o);
+ v = input_get (i);
- o = valve_state[i];
+ usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/POWER %s\r\n", valve_name[i], o ? "ON" : "OFF"));
+ usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/OPEN %d\r\n", valve_name[i], v));
- output_write (i, o);
- v = input_get (i);
+ usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/var1 %.2f\r\n", valve_name[i], scale (low_limit[i])));
- usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/POWER %s\r\n", valve_name[i], o ? "ON" : "OFF"));
- usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/OPEN %d\r\n", valve_name[i], v));
+ usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/var2 %.2f\r\n", valve_name[i], scale (high_limit[i])));
- usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/var1 %.2f\r\n", valve_name[i], scale(low_limit[i])));
- usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/var2 %.2f\r\n", valve_name[i], scale(high_limit[i])));
+ if (!read_failed) {
+ usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/TEMPERATURE %.2f\r\n", valve_name[i], scale (temp[i])));
+ usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/DELTA %.2f\r\n", valve_name[i], scale (low_limit[i] - temp[i])));
+ }
+ usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/failed_reads %d\r\n", valve_name[i], failed_reads[i]));
- if (!read_failed) {
- usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/TEMPERATURE %.2f\r\n", valve_name[i], scale(temp[i])));
- usart1_queue_buf (msg_buf, sprintf (msg_buf, "\r\nMQTTR stat/%s/DELTA %.2f\r\n", valve_name[i], scale(low_limit[i]-temp[i])));
+ } else {
+ valve_state[i] = 0;
+ o = valve_state[i];
+ output_write (i, o);
}
@@ -143,7 +202,12 @@ void logic_tick (void)
onewire_search();
}
- read_failed = ds18b20_set_12_convert (&valve_owa[i]);
+ if (!memcmp (&valve_owa[i], &owa_zero, sizeof (owa_zero)))
+ not_present = 1;
+ else {
+ not_present = 0;
+ read_failed = ds18b20_set_12_convert (&valve_owa[i]);
+ }
}
@@ -156,3 +220,31 @@ void logic_slow_tick (void)
void logic_dispatch (void)
{
}
+
+
+void logic_init (void)
+{
+ char serial[25] = {0};
+ unsigned i;
+
+ desig_get_unique_id_as_string (serial, sizeof (serial));
+
+ if (!strcmp (serial, "325715574155303632FFD305")) {
+ valve_name = plc0_valve_names;
+ valve_owa = plc0_valve_owas;
+
+ for (i = 0; i < 3; ++i) {
+ low_limit[i] = 19000;
+ high_limit[i] = 20000;
+ }
+ } else if (!strcmp (serial, "378125574155383139FFD805")) {
+ valve_name = plc1_valve_names;
+ valve_owa = plc1_valve_owas;
+ } else {
+ printf ("serial: %s\n", serial);
+ valve_name = plc0_valve_names;
+ valve_owa = plc0_valve_owas;
+ }
+
+
+}
diff --git a/radiator-plc/stm32/app/main.c b/radiator-plc/stm32/app/main.c
index 57b6752..4a00a70 100644
--- a/radiator-plc/stm32/app/main.c
+++ b/radiator-plc/stm32/app/main.c
@@ -19,6 +19,7 @@ static void mqtt_cmd (char *type)
type++;
who = index (type, ' ');
+
if (who) *who = 0;
@@ -133,11 +134,10 @@ main (void)
onewire_init();
inputs_init();
outputs_init();
+ logic_init();
printf ("D: RESET\n");
- //onewire_search();
-
for (;;) {
if (!ring_empty (&rx1_ring))
kbd_dispatch();
diff --git a/radiator-plc/stm32/app/project.h b/radiator-plc/stm32/app/project.h
index 85f14b9..f4f308a 100644
--- a/radiator-plc/stm32/app/project.h
+++ b/radiator-plc/stm32/app/project.h
@@ -2,6 +2,7 @@
#include <string.h>
#include <strings.h>
#include <libopencm3/stm32/rcc.h>
+#include <libopencm3/stm32/desig.h>
#include <libopencm3/stm32/gpio.h>
#include <libopencm3/stm32/usart.h>
#include <libopencm3/stm32/iwdg.h>
diff --git a/radiator-plc/stm32/app/prototypes.h b/radiator-plc/stm32/app/prototypes.h
index 2347f01..6c6f36a 100644
--- a/radiator-plc/stm32/app/prototypes.h
+++ b/radiator-plc/stm32/app/prototypes.h
@@ -68,6 +68,7 @@ extern void mqtt_dispatch(char *type, char *who, char *what, char *msg);
extern void logic_tick(void);
extern void logic_slow_tick(void);
extern void logic_dispatch(void);
+extern void logic_init(void);
/* ds18b20.c */
extern unsigned extract_leu16(uint8_t *d);
extern int extract_les16(uint8_t *d);
diff --git a/radiator-plc/stm32/app/rain.c b/radiator-plc/stm32/app/rain.c
deleted file mode 100644
index 4188990..0000000
--- a/radiator-plc/stm32/app/rain.c
+++ /dev/null
@@ -1,100 +0,0 @@
-#include "project.h"
-
-#define DEBOUNCE 20
-
-static uint8_t r_state;
-static uint32_t r_ticks;
-static uint8_t r_current;
-static uint8_t r_filtered;
-static unsigned r_expiry;
-
-
-#define RAIN_BANK GPIOA
-#define RAIN_GPIO GPIO15
-
-static int
-raw_rain (void)
-{
- return !gpio_get (RAIN_BANK, RAIN_GPIO);
-}
-
-
-void rain_notify (void)
-{
- if (r_current)
- vents_rain_notify();
-}
-
-
-void rain_ticker (void)
-{
- unsigned s;
- static int ticker = 0;
-
-
- s = raw_rain();
-
- if (s == r_state) {
- if (r_ticks < DEBOUNCE)
- r_ticks++;
- else if (r_ticks == DEBOUNCE) {
- r_current = s;
- r_ticks++;
- printf ("D: rain state now %d\r\n", r_current);
- }
- } else {
- r_ticks = 0;
- r_state = s;
- }
-
- if (r_current) {
- r_filtered = 1;
- r_expiry = 1800;
- }
-
- rain_notify();
-
- ticker++;
-
- if (ticker < 1000) return;
-
- ticker = 0;
-
- if (r_expiry)
- r_expiry--;
- else
- r_filtered = r_current;
-
-}
-
-void rain_slow_ticker (void)
-{
- printf ("R: %d %d\r\n", r_current, r_filtered);
-}
-
-
-int rain_filtered_state (void)
-{
- return r_filtered;
-}
-
-int rain_state (void)
-{
- return r_current;
-}
-
-char rain_state_char (void)
-{
- return r_current ? 'Y' : 'N';
-}
-
-void
-rain_init (void)
-{
- gpio_set_mode (RAIN_BANK, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN,
- RAIN_GPIO);
-
- gpio_set (RAIN_BANK, RAIN_GPIO);
-}
-
-
diff --git a/radiator-plc/stm32/app/ticker.c b/radiator-plc/stm32/app/ticker.c
index bb346fb..ffe63d0 100644
--- a/radiator-plc/stm32/app/ticker.c
+++ b/radiator-plc/stm32/app/ticker.c
@@ -47,7 +47,10 @@ sys_tick_handler (void)
slow++;
- if (slow < 2) return;
+ if (slow < 5) return;
+
+ slow = 0;
+
logic_tick();
diff --git a/set_radiator_target b/set_radiator_target
deleted file mode 100755
index 85ef1f9..0000000
--- a/set_radiator_target
+++ /dev/null
@@ -1,10 +0,0 @@
-#!/bin/bash
-R=$1
-T=$2
-H=10.32.139.1
-
-mosquitto_pub -h $H -t cmnd/$R/var1 -m $T
-sleep 1
-mosquitto_pub -h $H -t cmnd/$R/var2 -m $[ T + 1 ]
-
-
diff --git a/tasmota-config/configure-radiators b/tasmota-config/configure-radiators
index 488e7e3..6799acd 100755
--- a/tasmota-config/configure-radiators
+++ b/tasmota-config/configure-radiators
@@ -18,7 +18,7 @@ M=10.32.139.1
set -x
-for T in laundry_radiator ; do #kstudy_radiator bedroom_radiator spare_bedroom_radiator; do
+for T in laundry_radiator kstudy_radiator bedroom_radiator spare_bedroom_radiator; do
mosquitto_pub -h "${M}" -t "cmnd/${T}/Backlog" -m "Switchmode1 3; Rule1 1; Rule1 4; Rule2 1; Rule2 4; Rule3 1; Rule3 4"
sleep 3
mosquitto_pub -h "${M}" -t "cmnd/${T}/Backlog" -m "TelePeriod 60; SetOption26 0; SetOption0 0; PowerOnState 0"
@@ -32,8 +32,8 @@ for T in laundry_radiator ; do #kstudy_radiator bedroom_radiator spare_bedroom_r
ON system#boot DO Backlog RuleTimer1 70; var1 5; var2 5; var3 0; var4 0 ENDON
ON Rules#Timer=1 DO Backlog RuleTimer1 70; Power %var3% ENDON
ON tele-SI7021#temperature DO Backlog RuleTimer1 70; Event temp=%value% ENDON
- ON Switch1#State=0 DO Backlog var3 0; RuleTimer2 0; Event temp=100 ENDON
- ON Switch1#State=1 DO Backlog var3 1; RuleTimer2 1800; Event temp=100 ENDON
+ ON Button1#State DO event toggle3=%var3% ENDON
+ ON Button2#State DO event toggle3=%var3% ENDON
EOF
R="$(echo $R)"
@@ -43,8 +43,8 @@ EOF
##
read -r -d '' R << EOF
- ON Switch2#State=0 DO Backlog var3 0; RuleTimer2 0; Event temp=100 ENDON
- ON Switch2#State=1 DO Backlog var3 1; RuleTimer2 1800; Event temp=100 ENDON
+ ON event#toggle3<1 DO BackLog var3 1; RuleTimer2 1800; Event temp=100 ENDON
+ ON event#toggle3>0 DO BackLog var3 0; RuleTimer2 0; Event temp=100 ENDON
ON Rules#Timer=2 DO var3 0 ENDON
ON Power1#State DO BackLog Var4 %value%; Publish2 stat/${T}/OPEN %value% ENDON
ON Event#temp DO BackLog Publish2 stat/${T}/var1 %var1%; Publish2 stat/${T}/var2 %var2% ENDON
@@ -60,7 +60,7 @@ EOF
ON Event#temp<%var1% DO Power1 1 ENDON
ON Event#temp>%var2% DO Power1 %var3% ENDON
ON Event#temp<5 DO Power1 1 ENDON
- ON Event#temp DO BackLog Var5 %var1%; Sub5 %value%; Publish2 stat/${T}/DELTA %var5%; Publish2 stat/${T}/OPEN %var4% ENDON
+ ON Event#temp DO BackLog Var5 %var1%; Sub5 %value%; Publish2 stat/${T}/DELTA %var5%; Publish2 stat/${T}/OPEN %var4%; Publish2 stat/${T}/TEMPERATURE %value% ENDON
EOF
R="$(echo $R)"
diff --git a/watch b/watch
new file mode 100755
index 0000000..67b88c9
--- /dev/null
+++ b/watch
@@ -0,0 +1,6 @@
+#!/bin/bash
+H=10.32.139.1
+
+mosquitto_sub -h $H -t stat/+/OPEN -v
+
+