From 9d87c925a9eaa4fc256be3173c14a20d1469472d Mon Sep 17 00:00:00 2001 From: fishsoupisgood Date: Wed, 9 Sep 2020 11:53:37 +0100 Subject: everything, mostly, working --- .gitmodules | 6 + .otmonitor.config | 130 + DOCS/41hpa-warranty-registration.pdf | Bin 37952 -> 0 bytes DOCS/Opentherm Protocol v2-2.pdf | Bin 285534 -> 0 bytes boiler-monster/DOCS/IMG_20200514_1758578.jpg | Bin 0 -> 3034508 bytes boiler-monster/DOCS/Opentherm Protocol v2-2.pdf | Bin 0 -> 285534 bytes boiler-monster/mr3020/.gitignore | 1 + boiler-monster/mr3020/Makefile | 5 + boiler-monster/mr3020/etc/init.d/stm32 | 7 + boiler-monster/mr3020/etc/rc.d/S99stm32 | 1 + boiler-monster/mr3020/etc/stm32/startup | 50 + boiler-monster/mr3020/stamp | 0 boiler-monster/mr3020/usr/bin/thermostat | 70 + boiler-monster/original-pic-4.2.5/.gitignore | 6 + boiler-monster/original-pic-4.2.5/16f88.lkr | 76 + boiler-monster/original-pic-4.2.5/Makefile | 22 + boiler-monster/original-pic-4.2.5/build.asm | 2 + boiler-monster/original-pic-4.2.5/build.vbs | 77 + boiler-monster/original-pic-4.2.5/ds1820.asm | 198 + boiler-monster/original-pic-4.2.5/gateway.asm | 4524 ++++++++++++++++++++ boiler-monster/original-pic-4.2.5/license.txt | 58 + boiler-monster/original-pic-4.2.5/ot-gateway.brd | Bin 0 -> 37424 bytes boiler-monster/original-pic-4.2.5/ot-gateway.sch | Bin 0 -> 210692 bytes boiler-monster/original-pic-4.2.5/readme.txt | 43 + boiler-monster/original-pic-4.2.5/selfprog.asm | 391 ++ boiler-monster/pic/.gitignore | 8 + boiler-monster/pic/16f88.lkr | 76 + boiler-monster/pic/Makefile | 22 + boiler-monster/pic/build.asm | 2 + boiler-monster/pic/docs/otgw-big-sch.png | Bin 0 -> 77271 bytes boiler-monster/pic/docs/pinout.txt | 33 + boiler-monster/pic/gateway.asm | 296 ++ boiler-monster/pic/selfprog.asm | 391 ++ boiler-monster/stm32/.gitignore | 15 + boiler-monster/stm32/Makefile | 8 + boiler-monster/stm32/Makefile.include | 44 + boiler-monster/stm32/Makefile.rules | 261 ++ boiler-monster/stm32/app/1wire.c | 392 ++ boiler-monster/stm32/app/1wire.h | 10 + boiler-monster/stm32/app/Makefile | 113 + boiler-monster/stm32/app/adc.c | 154 + boiler-monster/stm32/app/boiler.ld | 31 + boiler-monster/stm32/app/cmd.c | 55 + boiler-monster/stm32/app/commit.c | 3 + boiler-monster/stm32/app/ds1820.c | 93 + boiler-monster/stm32/app/gdb.script | 2 + boiler-monster/stm32/app/led.c | 101 + boiler-monster/stm32/app/main.c | 134 + boiler-monster/stm32/app/ot.c | 452 ++ boiler-monster/stm32/app/ot_phy_rx.c | 177 + boiler-monster/stm32/app/ot_phy_tx.c | 121 + boiler-monster/stm32/app/pic.c | 52 + boiler-monster/stm32/app/pins.h | 53 + boiler-monster/stm32/app/pressure.c | 164 + boiler-monster/stm32/app/project.h | 30 + boiler-monster/stm32/app/prototypes.h | 106 + boiler-monster/stm32/app/ring.c | 77 + boiler-monster/stm32/app/ring.h | 13 + boiler-monster/stm32/app/stdio.c | 77 + boiler-monster/stm32/app/temp.c | 76 + boiler-monster/stm32/app/ticker.c | 105 + boiler-monster/stm32/app/usart.c | 175 + boiler-monster/stm32/app/util.c | 14 + boiler-monster/stm32/docs/pinout.txt | 24 + boiler-monster/stm32/docs/pm0056.pdf | Bin 0 -> 2098835 bytes boiler-monster/stm32/docs/rm0008.pdf | Bin 0 -> 13016697 bytes boiler-monster/stm32/docs/stm32f103c8.pdf | Bin 0 -> 1697666 bytes .../stm32/docs/stm32f103c8t6_pinout_voltage01.png | Bin 0 -> 679565 bytes boiler-monster/stm32/libopencm3 | 1 + boiler/41hpa-warranty-registration.pdf | Bin 0 -> 37952 bytes boiler/773447801-baxi-platinum-40-user.pdf | Bin 0 -> 508408 bytes ...xi-platinum-installation-and-service-manual.pdf | Bin 0 -> 6386210 bytes munin/.gitignore | 1 + munin/boiler | 552 +++ munin/boiler_rx | 65 + otmonitor | 2 +- pic/.gitignore | 8 - pic/16f88.lkr | 76 - pic/Makefile | 22 - pic/build.asm | 2 - pic/docs/otgw-big-sch.png | Bin 77271 -> 0 bytes pic/docs/pinout.txt | 33 - pic/gateway.asm | 296 -- pic/selfprog.asm | 391 -- radiator-plc/DOCS/DS18B20.pdf | Bin 0 -> 392186 bytes radiator-plc/DOCS/plc_notes.txt | 93 + radiator-plc/DOCS/radiator-numbering.txt | 8 + radiator-plc/DOCS/valve_01213210.pdf | Bin 0 -> 341645 bytes radiator-plc/DOCS/valve_wiring.txt | 15 + radiator-plc/mt300n-v2/.gitignore | 1 + radiator-plc/mt300n-v2/Makefile | 6 + radiator-plc/mt300n-v2/usr/bin/mqtt_if.pl | 76 + radiator-plc/mt300n-v2/usr/bin/run_mqtt_if | 6 + radiator-plc/stm32/.gitignore | 15 + radiator-plc/stm32/Makefile | 8 + radiator-plc/stm32/Makefile.include | 44 + radiator-plc/stm32/Makefile.rules | 261 ++ radiator-plc/stm32/app/1wire.c | 384 ++ radiator-plc/stm32/app/1wire.h | 9 + radiator-plc/stm32/app/Makefile | 59 + radiator-plc/stm32/app/control.c | 114 + radiator-plc/stm32/app/ds18b20.c | 128 + radiator-plc/stm32/app/gdb.script | 2 + radiator-plc/stm32/app/hexdump.c | 57 + radiator-plc/stm32/app/inputs.c | 26 + radiator-plc/stm32/app/led.c | 53 + radiator-plc/stm32/app/logic.c | 158 + radiator-plc/stm32/app/main.c | 150 + radiator-plc/stm32/app/outputs.c | 47 + radiator-plc/stm32/app/pins.h | 53 + radiator-plc/stm32/app/project.h | 29 + radiator-plc/stm32/app/prototypes.h | 80 + radiator-plc/stm32/app/radiator-plc.ld | 38 + radiator-plc/stm32/app/rain.c | 100 + radiator-plc/stm32/app/ring.c | 69 + radiator-plc/stm32/app/ring.h | 6 + radiator-plc/stm32/app/ticker.c | 97 + radiator-plc/stm32/app/usart.c | 149 + radiator-plc/stm32/app/watchdog.c | 16 + radiator-plc/stm32/libopencm3 | 1 + run_ot_monitor | 4 + set_radiator_target | 10 + stm32/.gitignore | 16 - stm32/Makefile | 8 - stm32/Makefile.include | 44 - stm32/Makefile.rules | 261 -- stm32/app/1wire.c | 392 -- stm32/app/1wire.h | 10 - stm32/app/Makefile | 132 - stm32/app/adc.c | 121 - stm32/app/boiler.ld | 31 - stm32/app/cmd.c | 55 - stm32/app/commit.c | 3 - stm32/app/ds1820.c | 93 - stm32/app/gdb.script | 2 - stm32/app/led.c | 101 - stm32/app/main.c | 135 - stm32/app/ot.c | 441 -- stm32/app/ot_phy_rx.c | 177 - stm32/app/ot_phy_tx.c | 121 - stm32/app/pic.c | 52 - stm32/app/pins.h | 53 - stm32/app/pressure.c | 79 - stm32/app/project.h | 30 - stm32/app/prototypes.h | 103 - stm32/app/ring.c | 77 - stm32/app/ring.h | 13 - stm32/app/stdio.c | 77 - stm32/app/temp.c | 76 - stm32/app/ticker.c | 103 - stm32/app/usart.c | 175 - stm32/app/util.c | 14 - stm32/docs/pinout.txt | 24 - stm32/docs/pm0056.pdf | Bin 2098835 -> 0 bytes stm32/docs/rm0008.pdf | Bin 13016697 -> 0 bytes stm32/docs/stm32f103c8.pdf | Bin 1697666 -> 0 bytes stm32/docs/stm32f103c8t6_pinout_voltage01.png | Bin 679565 -> 0 bytes stm32/libopencm3 | 1 - tasmota-config/configure-radiators | 74 + 159 files changed, 12693 insertions(+), 3849 deletions(-) create mode 100644 .otmonitor.config delete mode 100644 DOCS/41hpa-warranty-registration.pdf delete mode 100644 DOCS/Opentherm Protocol v2-2.pdf create mode 100644 boiler-monster/DOCS/IMG_20200514_1758578.jpg create mode 100644 boiler-monster/DOCS/Opentherm Protocol v2-2.pdf create mode 100644 boiler-monster/mr3020/.gitignore create mode 100644 boiler-monster/mr3020/Makefile create mode 100755 boiler-monster/mr3020/etc/init.d/stm32 create mode 120000 boiler-monster/mr3020/etc/rc.d/S99stm32 create mode 100755 boiler-monster/mr3020/etc/stm32/startup create mode 100644 boiler-monster/mr3020/stamp create mode 100755 boiler-monster/mr3020/usr/bin/thermostat create mode 100644 boiler-monster/original-pic-4.2.5/.gitignore create mode 100644 boiler-monster/original-pic-4.2.5/16f88.lkr create mode 100644 boiler-monster/original-pic-4.2.5/Makefile create mode 100644 boiler-monster/original-pic-4.2.5/build.asm create mode 100644 boiler-monster/original-pic-4.2.5/build.vbs create mode 100644 boiler-monster/original-pic-4.2.5/ds1820.asm create mode 100644 boiler-monster/original-pic-4.2.5/gateway.asm create mode 100644 boiler-monster/original-pic-4.2.5/license.txt create mode 100644 boiler-monster/original-pic-4.2.5/ot-gateway.brd create mode 100644 boiler-monster/original-pic-4.2.5/ot-gateway.sch create mode 100644 boiler-monster/original-pic-4.2.5/readme.txt create mode 100644 boiler-monster/original-pic-4.2.5/selfprog.asm create mode 100644 boiler-monster/pic/.gitignore create mode 100644 boiler-monster/pic/16f88.lkr create mode 100644 boiler-monster/pic/Makefile create mode 100644 boiler-monster/pic/build.asm create mode 100644 boiler-monster/pic/docs/otgw-big-sch.png create mode 100644 boiler-monster/pic/docs/pinout.txt create mode 100644 boiler-monster/pic/gateway.asm create mode 100644 boiler-monster/pic/selfprog.asm create mode 100644 boiler-monster/stm32/.gitignore create mode 100644 boiler-monster/stm32/Makefile create mode 100644 boiler-monster/stm32/Makefile.include create mode 100644 boiler-monster/stm32/Makefile.rules create mode 100644 boiler-monster/stm32/app/1wire.c create mode 100644 boiler-monster/stm32/app/1wire.h create mode 100644 boiler-monster/stm32/app/Makefile create mode 100644 boiler-monster/stm32/app/adc.c create mode 100644 boiler-monster/stm32/app/boiler.ld create mode 100644 boiler-monster/stm32/app/cmd.c create mode 100644 boiler-monster/stm32/app/commit.c create mode 100644 boiler-monster/stm32/app/ds1820.c create mode 100644 boiler-monster/stm32/app/gdb.script create mode 100644 boiler-monster/stm32/app/led.c create mode 100644 boiler-monster/stm32/app/main.c create mode 100644 boiler-monster/stm32/app/ot.c create mode 100644 boiler-monster/stm32/app/ot_phy_rx.c create mode 100644 boiler-monster/stm32/app/ot_phy_tx.c create mode 100644 boiler-monster/stm32/app/pic.c create mode 100644 boiler-monster/stm32/app/pins.h create mode 100644 boiler-monster/stm32/app/pressure.c create mode 100644 boiler-monster/stm32/app/project.h create mode 100644 boiler-monster/stm32/app/prototypes.h create mode 100644 boiler-monster/stm32/app/ring.c create mode 100644 boiler-monster/stm32/app/ring.h create mode 100644 boiler-monster/stm32/app/stdio.c create mode 100644 boiler-monster/stm32/app/temp.c create mode 100644 boiler-monster/stm32/app/ticker.c create mode 100644 boiler-monster/stm32/app/usart.c create mode 100644 boiler-monster/stm32/app/util.c create mode 100644 boiler-monster/stm32/docs/pinout.txt create mode 100644 boiler-monster/stm32/docs/pm0056.pdf create mode 100644 boiler-monster/stm32/docs/rm0008.pdf create mode 100644 boiler-monster/stm32/docs/stm32f103c8.pdf create mode 100644 boiler-monster/stm32/docs/stm32f103c8t6_pinout_voltage01.png create mode 160000 boiler-monster/stm32/libopencm3 create mode 100644 boiler/41hpa-warranty-registration.pdf create mode 100644 boiler/773447801-baxi-platinum-40-user.pdf create mode 100644 boiler/baxi-platinum-installation-and-service-manual.pdf create mode 100644 munin/.gitignore create mode 100755 munin/boiler create mode 100755 munin/boiler_rx delete mode 100644 pic/.gitignore delete mode 100644 pic/16f88.lkr delete mode 100644 pic/Makefile delete mode 100644 pic/build.asm delete mode 100644 pic/docs/otgw-big-sch.png delete mode 100644 pic/docs/pinout.txt delete mode 100644 pic/gateway.asm delete mode 100644 pic/selfprog.asm create mode 100644 radiator-plc/DOCS/DS18B20.pdf create mode 100644 radiator-plc/DOCS/plc_notes.txt create mode 100644 radiator-plc/DOCS/radiator-numbering.txt create mode 100644 radiator-plc/DOCS/valve_01213210.pdf create mode 100644 radiator-plc/DOCS/valve_wiring.txt create mode 100644 radiator-plc/mt300n-v2/.gitignore create mode 100644 radiator-plc/mt300n-v2/Makefile create mode 100755 radiator-plc/mt300n-v2/usr/bin/mqtt_if.pl create mode 100755 radiator-plc/mt300n-v2/usr/bin/run_mqtt_if create mode 100644 radiator-plc/stm32/.gitignore create mode 100644 radiator-plc/stm32/Makefile create mode 100644 radiator-plc/stm32/Makefile.include create mode 100644 radiator-plc/stm32/Makefile.rules create mode 100644 radiator-plc/stm32/app/1wire.c create mode 100644 radiator-plc/stm32/app/1wire.h create mode 100644 radiator-plc/stm32/app/Makefile create mode 100644 radiator-plc/stm32/app/control.c create mode 100644 radiator-plc/stm32/app/ds18b20.c create mode 100644 radiator-plc/stm32/app/gdb.script create mode 100644 radiator-plc/stm32/app/hexdump.c create mode 100644 radiator-plc/stm32/app/inputs.c create mode 100644 radiator-plc/stm32/app/led.c create mode 100644 radiator-plc/stm32/app/logic.c create mode 100644 radiator-plc/stm32/app/main.c create mode 100644 radiator-plc/stm32/app/outputs.c create mode 100644 radiator-plc/stm32/app/pins.h create mode 100644 radiator-plc/stm32/app/project.h create mode 100644 radiator-plc/stm32/app/prototypes.h create mode 100644 radiator-plc/stm32/app/radiator-plc.ld create mode 100644 radiator-plc/stm32/app/rain.c create mode 100644 radiator-plc/stm32/app/ring.c create mode 100644 radiator-plc/stm32/app/ring.h create mode 100644 radiator-plc/stm32/app/ticker.c create mode 100644 radiator-plc/stm32/app/usart.c create mode 100644 radiator-plc/stm32/app/watchdog.c create mode 160000 radiator-plc/stm32/libopencm3 create mode 100755 run_ot_monitor create mode 100755 set_radiator_target delete mode 100644 stm32/.gitignore delete mode 100644 stm32/Makefile delete mode 100644 stm32/Makefile.include delete mode 100644 stm32/Makefile.rules delete mode 100644 stm32/app/1wire.c delete mode 100644 stm32/app/1wire.h delete mode 100644 stm32/app/Makefile delete mode 100644 stm32/app/adc.c delete mode 100644 stm32/app/boiler.ld delete mode 100644 stm32/app/cmd.c delete mode 100644 stm32/app/commit.c delete mode 100644 stm32/app/ds1820.c delete mode 100644 stm32/app/gdb.script delete mode 100644 stm32/app/led.c delete mode 100644 stm32/app/main.c delete mode 100644 stm32/app/ot.c delete mode 100644 stm32/app/ot_phy_rx.c delete mode 100644 stm32/app/ot_phy_tx.c delete mode 100644 stm32/app/pic.c delete mode 100644 stm32/app/pins.h delete mode 100644 stm32/app/pressure.c delete mode 100644 stm32/app/project.h delete mode 100644 stm32/app/prototypes.h delete mode 100644 stm32/app/ring.c delete mode 100644 stm32/app/ring.h delete mode 100644 stm32/app/stdio.c delete mode 100644 stm32/app/temp.c delete mode 100644 stm32/app/ticker.c delete mode 100644 stm32/app/usart.c delete mode 100644 stm32/app/util.c delete mode 100644 stm32/docs/pinout.txt delete mode 100644 stm32/docs/pm0056.pdf delete mode 100644 stm32/docs/rm0008.pdf delete mode 100644 stm32/docs/stm32f103c8.pdf delete mode 100644 stm32/docs/stm32f103c8t6_pinout_voltage01.png delete mode 160000 stm32/libopencm3 create mode 100755 tasmota-config/configure-radiators diff --git a/.gitmodules b/.gitmodules index 4d99ada..6011582 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,9 @@ [submodule "stm32/libopencm3"] path = stm32/libopencm3 url = git://git.panaceas.org/stm32/libopencm3 +[submodule "radiator-plc/stm32/libopencm3"] + path = radiator-plc/stm32/libopencm3 + url = git://git.panaceas.org/stm32/libopencm3 +[submodule "boiler-monster/stm32/libopencm3"] + path = boiler-monster/stm32/libopencm3 + url = git://git.panaceas.org/stm32/libopencm3 diff --git a/.otmonitor.config b/.otmonitor.config new file mode 100644 index 0000000..c426c03 --- /dev/null +++ b/.otmonitor.config @@ -0,0 +1,130 @@ +datalog { + append false + file /root/otdata.txt + enable false + itemlist { + flame + dhwmode + chmode + dhwenable + diag + fault + outside + roomtemp + setpoint + modulation + boilertemp + returntemp + controlsp + dhwsetpoint + chwsetpoint + timestamp + feedtemp + } + interval 30000 +} +web { + sslport 0 + graphlegend false + theme default + enable false + sslprotocols tls1,tls1.1,tls1.2 + nopass true + certonly false + port 8080 +} +clock { + year false + date false + auto false +} +sms { + enable false + ventilationfault false + phonenumber {} + route {} + sender {} + password c2VjcmV0 + provider VoipPlanet + account {} + pressure false + solarfault false + roomcold true + watchdogtimer false + boilerfault true + commproblem false +} +email { + password c2VjcmV0 + server {} + pressure true + roomcold true + boilerfault true + watchdogtimer true + solarfault true + commproblem true + port 25 + enable false + secure TLS + recipient {} + user {} + sender {} + ventilationfault true +} +mqtt { + retransmit 10 + format json2 + port 1883 + qos 1 + keepalive 120 + eventtopic events/central_heating/otmonitor + username {} + actiontopic actions/otmonitor + password {} + enable false + messages false + client new-circe-otmon + broker localhost +} +logfile { + pattern otlog-%Y%m%d.txt + directory /root + enable false +} +tspeak { + key {} + sync false + enable false + interval 120 + field1 roomtemp + field2 setpoint + field3 boilertemp + field4 returntemp + field5 controlsp + field6 modulation + field7 {} + field8 {} +} +alert { + pressurelow 1.5 + roomcold 14 + pressurehigh 2.0 +} +connection { + device /dev/ttyUSB0 + type tcp + port 2001 + host 10.32.139.18 + enable true +} +server { + relay false + enable true + port 7686 +} +view { + tab graph + order increasing + sort name + bitflags false +} diff --git a/DOCS/41hpa-warranty-registration.pdf b/DOCS/41hpa-warranty-registration.pdf deleted file mode 100644 index f42b06d..0000000 Binary files a/DOCS/41hpa-warranty-registration.pdf and /dev/null differ diff --git a/DOCS/Opentherm Protocol v2-2.pdf b/DOCS/Opentherm Protocol v2-2.pdf deleted file mode 100644 index 47a7f59..0000000 Binary files a/DOCS/Opentherm Protocol v2-2.pdf and /dev/null differ diff --git a/boiler-monster/DOCS/IMG_20200514_1758578.jpg b/boiler-monster/DOCS/IMG_20200514_1758578.jpg new file mode 100644 index 0000000..5f159c5 Binary files /dev/null and b/boiler-monster/DOCS/IMG_20200514_1758578.jpg differ diff --git a/boiler-monster/DOCS/Opentherm Protocol v2-2.pdf b/boiler-monster/DOCS/Opentherm Protocol v2-2.pdf new file mode 100644 index 0000000..47a7f59 Binary files /dev/null and b/boiler-monster/DOCS/Opentherm Protocol v2-2.pdf differ diff --git a/boiler-monster/mr3020/.gitignore b/boiler-monster/mr3020/.gitignore new file mode 100644 index 0000000..859afb1 --- /dev/null +++ b/boiler-monster/mr3020/.gitignore @@ -0,0 +1 @@ +stamp diff --git a/boiler-monster/mr3020/Makefile b/boiler-monster/mr3020/Makefile new file mode 100644 index 0000000..26efbc3 --- /dev/null +++ b/boiler-monster/mr3020/Makefile @@ -0,0 +1,5 @@ +STUFF=$(shell find etc usr \! -type d -print) + +stamp: ${STUFF} + tar cf - ${STUFF} | ssh boiler-monster "cd / && tar xvpf -" + touch $@ diff --git a/boiler-monster/mr3020/etc/init.d/stm32 b/boiler-monster/mr3020/etc/init.d/stm32 new file mode 100755 index 0000000..6dce9bf --- /dev/null +++ b/boiler-monster/mr3020/etc/init.d/stm32 @@ -0,0 +1,7 @@ +#!/bin/sh /etc/rc.common +# Copyright (C) 2006 OpenWrt.org + +START=99 +boot() { + /etc/stm32/startup > /var/log/stm32.log +} diff --git a/boiler-monster/mr3020/etc/rc.d/S99stm32 b/boiler-monster/mr3020/etc/rc.d/S99stm32 new file mode 120000 index 0000000..a3b0774 --- /dev/null +++ b/boiler-monster/mr3020/etc/rc.d/S99stm32 @@ -0,0 +1 @@ +../init.d/stm32 \ No newline at end of file diff --git a/boiler-monster/mr3020/etc/stm32/startup b/boiler-monster/mr3020/etc/stm32/startup new file mode 100755 index 0000000..9422745 --- /dev/null +++ b/boiler-monster/mr3020/etc/stm32/startup @@ -0,0 +1,50 @@ +#!/bin/sh + +logger -t stm32 "startup" + +LOCK=/var/lock/LCK..ttyATH0 +echo $$ > ${LOCK}.tmp + +#killall -9 ser2net + +FW="/etc/stm32/boiler.fw" +TFW="/tmp/stm32.fw" + +PORT="/dev/ttyATH0" + +echo 5 > /sys/class/gpio/export +echo out > /sys/class/gpio/gpio5/direction + +for i in $(seq 0 20); do + echo 1 > /sys/class/leds/tp-link\:green\:wps/brightness + echo 0 > /sys/class/leds/tp-link\:green\:wps/brightness + + if stm32flash "${PORT}"; then + break + fi + sleep 1 +done + +if [ -z "$1" ]; then + logger -t stm32 "reading ROM" + if ! stm32flash -r "${TFW}" "${PORT}" ; then + exit 1 + fi + + if diff -q "${TFW}" "${FW}"; then + logger -t stm32 "Code in ROM matches file" + echo "Code in ROM matches file" + else + logger -t stm32 "Flashing" + echo "Code in ROM doesn't match file, flashing" + stm32flash -w "${FW}" "${PORT}" + fi +else + echo "Flashing..." + stm32flash -w "$1" "${PORT}" +fi + +logger -t stm32 "Booting" +stm32flash -g 0 "${PORT}" + +rm -f ${LOCK} diff --git a/boiler-monster/mr3020/stamp b/boiler-monster/mr3020/stamp new file mode 100644 index 0000000..e69de29 diff --git a/boiler-monster/mr3020/usr/bin/thermostat b/boiler-monster/mr3020/usr/bin/thermostat new file mode 100755 index 0000000..1689964 --- /dev/null +++ b/boiler-monster/mr3020/usr/bin/thermostat @@ -0,0 +1,70 @@ +#!/bin/sh + +LOCK=/var/lock/LCK..ttyATH0 +M=10.32.139.1 + +WATERS=0 +MAX=70 + +# conventions +# POWER is power to valve coil +# OPEN is valve state +# DELTA is temp difference +# var1 is low set point +# var2 is high set point +# var3 is manual override + +# towel radiators + +for i in laundry_radiator bathroom_radiator; do + O="$(mosquitto_sub -t stat/$i/OPEN -h ${M} -W 1 -C 1)" + + W=0 + if [ "$O" == "1" ]; then + W=$MAX + fi + WATERS="$WATERS $W" + + logger -t thermostat " $i O=$O W=$W" +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 + 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') " + + W=0 + if [ "$O" == "1" ]; then + W=$[ $D * 3 + 40] + fi + + logger -t thermostat " $i O=$O D=$D W=$W" + + WATERS="$WATERS $W" +done + + +R=0 +for w in ${WATERS}; do + if [ $w -gt $R ]; then + R=$w + fi +done + +if [ $R -gt $MAX ]; then + R=$MAX +fi + + + + +if [ ! -f "${LOCK}" ]; then + logger -t thermostat "Requesting water temp of $R" + printf "\nCH=%d\n" $R > /dev/ttyATH0 +else + logger -t thermostat "Interface is locked, so cant request water temp of $R" +fi + + + diff --git a/boiler-monster/original-pic-4.2.5/.gitignore b/boiler-monster/original-pic-4.2.5/.gitignore new file mode 100644 index 0000000..86351a7 --- /dev/null +++ b/boiler-monster/original-pic-4.2.5/.gitignore @@ -0,0 +1,6 @@ +*.err +*.lst +*.o +*.hex +*.map +*.cod diff --git a/boiler-monster/original-pic-4.2.5/16f88.lkr b/boiler-monster/original-pic-4.2.5/16f88.lkr new file mode 100644 index 0000000..b2b3e95 --- /dev/null +++ b/boiler-monster/original-pic-4.2.5/16f88.lkr @@ -0,0 +1,76 @@ +// File: 16F88_g.lkr +// Generic linker script for the PIC16F88 processor + +#IFDEF _DEBUG + +LIBPATH . + +CODEPAGE NAME=page0 START=0x0 END=0x7FF +CODEPAGE NAME=page1 START=0x800 END=0xEFF +CODEPAGE NAME=debug START=0xF00 END=0xFFF PROTECTED +CODEPAGE NAME=.idlocs START=0x2000 END=0x2003 PROTECTED +CODEPAGE NAME=.device_id START=0x2006 END=0x2006 PROTECTED +CODEPAGE NAME=.config START=0x2007 END=0x2009 PROTECTED +CODEPAGE NAME=eedata START=0x2100 END=0x21FF PROTECTED + +DATABANK NAME=sfr0 START=0x0 END=0x1F PROTECTED +DATABANK NAME=sfr1 START=0x80 END=0x9F PROTECTED +DATABANK NAME=sfr2 START=0x100 END=0x10F PROTECTED +DATABANK NAME=sfr3 START=0x180 END=0x18F PROTECTED + +SHAREBANK NAME=dbgnobnk START=0x70 END=0x70 PROTECTED +SHAREBANK NAME=dbgnobnk START=0xF0 END=0xF0 PROTECTED +SHAREBANK NAME=dbgnobnk START=0x170 END=0x170 PROTECTED +SHAREBANK NAME=dbgnobnk START=0x1F0 END=0x1F0 PROTECTED + +SHAREBANK NAME=sfrnobnk START=0x71 END=0x7F +SHAREBANK NAME=sfrnobnk START=0xF1 END=0xFF PROTECTED +SHAREBANK NAME=sfrnobnk START=0x171 END=0x17F PROTECTED +SHAREBANK NAME=sfrnobnk START=0x1F1 END=0x1FF PROTECTED + +DATABANK NAME=gpr0 START=0x20 END=0x6F +DATABANK NAME=gpr1 START=0xA0 END=0xEF +DATABANK NAME=gpr2 START=0x110 END=0x16F +DATABANK NAME=gpr3 START=0x190 END=0x1E4 +DATABANK NAME=dbg3 START=0x1E5 END=0x1EF PROTECTED + +SECTION NAME=PROG0 ROM=page0 // ROM code space +SECTION NAME=PROG1 ROM=page1 // ROM code space +SECTION NAME=DEBUG ROM=debug // ICD debug executive +SECTION NAME=IDLOCS ROM=.idlocs // ID locations +SECTION NAME=DEVICEID ROM=.device_id // Device ID +SECTION NAME=DEEPROM ROM=eedata // Data EEPROM + +#ELSE + +LIBPATH . + +CODEPAGE NAME=page0 START=0x0 END=0x7FF +CODEPAGE NAME=page1 START=0x800 END=0xFFF +CODEPAGE NAME=.idlocs START=0x2000 END=0x2003 PROTECTED +CODEPAGE NAME=.device_id START=0x2006 END=0x2006 PROTECTED +CODEPAGE NAME=.config START=0x2007 END=0x2009 PROTECTED +CODEPAGE NAME=eedata START=0x2100 END=0x21FF PROTECTED + +DATABANK NAME=sfr0 START=0x0 END=0x1F PROTECTED +DATABANK NAME=sfr1 START=0x80 END=0x9F PROTECTED +DATABANK NAME=sfr2 START=0x100 END=0x10F PROTECTED +DATABANK NAME=sfr3 START=0x180 END=0x18F PROTECTED + +SHAREBANK NAME=sfrnobnk START=0x70 END=0x7F +SHAREBANK NAME=sfrnobnk START=0xF0 END=0xFF PROTECTED +SHAREBANK NAME=sfrnobnk START=0x170 END=0x17F PROTECTED +SHAREBANK NAME=sfrnobnk START=0x1F0 END=0x1FF PROTECTED + +DATABANK NAME=gpr0 START=0x20 END=0x6F +DATABANK NAME=gpr1 START=0xA0 END=0xEF +DATABANK NAME=gpr2 START=0x110 END=0x16F +DATABANK NAME=gpr3 START=0x190 END=0x1EF + +SECTION NAME=PROG0 ROM=page0 // ROM code space +SECTION NAME=PROG1 ROM=page1 // ROM code space +SECTION NAME=IDLOCS ROM=.idlocs // ID locations +SECTION NAME=DEVICEID ROM=.device_id // Device ID +SECTION NAME=DEEPROM ROM=eedata // Data EEPROM + +#FI diff --git a/boiler-monster/original-pic-4.2.5/Makefile b/boiler-monster/original-pic-4.2.5/Makefile new file mode 100644 index 0000000..ec282b1 --- /dev/null +++ b/boiler-monster/original-pic-4.2.5/Makefile @@ -0,0 +1,22 @@ +AFLAGS=-k -p 16F88 -k +LDFLAGS=-m -s 16f88.lkr + +PROG=gateway +ASRCS=${PROG}.asm ds1820.asm selfprog.asm +OBJS=${ASRCS:%.asm=%.o} +LSTS=${ASRCS:%.asm=%.err} +ERRS=${ASRCS:%.asm=%.lst} + +default:${PROG}.hex + +%.o:%.asm + gpasm ${AFLAGS} -c -o $@ $< + +${PROG}.hex:${OBJS} + gplink ${LDFLAGS} -o $@ ${OBJS} + + +clean: + /bin/rm -f ${PROG}.hex ${PROG}.map ${PROG}.cod ${OBJS} ${LSTS} ${ERRS} + + diff --git a/boiler-monster/original-pic-4.2.5/build.asm b/boiler-monster/original-pic-4.2.5/build.asm new file mode 100644 index 0000000..67d0747 --- /dev/null +++ b/boiler-monster/original-pic-4.2.5/build.asm @@ -0,0 +1,2 @@ +#define build "1" +#define tstamp "17:59 20-10-2015" diff --git a/boiler-monster/original-pic-4.2.5/build.vbs b/boiler-monster/original-pic-4.2.5/build.vbs new file mode 100644 index 0000000..989fbf1 --- /dev/null +++ b/boiler-monster/original-pic-4.2.5/build.vbs @@ -0,0 +1,77 @@ +'Build.vbs +'Generate MPLAB include script +'----------------------------- +' +Option Explicit + +Dim sh, fso, src, fd +Dim ts, name, timestr +Dim proj, regpath, build + +Const incfile = "build.asm" + +Set sh = CreateObject("WScript.Shell") +Set fso = CreateObject("Scripting.FileSystemObject") + +'Make sure to be working in the project directory +sh.CurrentDirectory = fso.GetParentFolderName(Wscript.ScriptFullName) + +'Determine the last modification time of the sources +ts = DateValue("1/1/1970") +For Each name In fso.GetFolder(".").Files + If LCase(fso.GetExtensionName(name)) = "asm" Then + If fso.GetFileName(name) <> incfile Then + Set src = fso.GetFile(name) + If DateDiff("S", ts, src.DateLastModified) > 0 Then + ts = src.DateLastModified + End If + End If + End If +Next + +'Check if the include file needs to be regenerated +If fso.FileExists(incfile) Then + Set src = fso.GetFile(incfile) + If DateDiff("S", ts, src.DateLastModified) >= 0 Then + Wscript.StdOut.WriteLine "Source was not changed" + Wscript.Quit + End If +End If + +'Format the last modification time +timestr = right("0" & hour(ts), 2) & ":" & _ + right("0" & minute(ts), 2) & " " & _ + right("0" & day(ts), 2) & "-" & _ + right("0" & month(ts), 2) & "-" & year(ts) + +'Determine the project name +If WScript.Arguments.Count > 0 Then + 'The project name was specified on the command line + proj = WScript.Arguments.item(0) +Else + 'Derive the project name from the current directory + proj = fso.GetFileName(fso.GetAbsolutePathName(".")) +End If + +'Get the build revision from the registry +regpath = "HKLM\SOFTWARE\Microchip\MPLAB IDE\Projects\" & proj & "\Revision" +On Error Resume Next +build = sh.RegRead(regpath) +If Hex(Err.number) = "80070002" Then + build = 1 +Else + build = build + 1 +End If +On Error GoTo 0 +'Update the registry +sh.RegWrite regpath, build, "REG_DWORD" + +'Create the build.asm file +Set fd = fso.CreateTextFile(incfile) +fd.WriteLine "#define build """ & build & """" +fd.WriteLine "#define tstamp """ & timestr & """" +fd.Close + +'Report the build revision to standard output as well +Wscript.StdOut.WriteLine "Build revision: " & build +Wscript.StdOut.WriteLine "Build time & date: " & timestr diff --git a/boiler-monster/original-pic-4.2.5/ds1820.asm b/boiler-monster/original-pic-4.2.5/ds1820.asm new file mode 100644 index 0000000..b9b06c1 --- /dev/null +++ b/boiler-monster/original-pic-4.2.5/ds1820.asm @@ -0,0 +1,198 @@ + title "Outside sensor" + list p=16F88, b=8, r=dec + +#include "p16f88.inc" + errorlevel -302, -306 + +#define IOPORT PORTA,7 ;I/O port used for 1-wire line + +;1-wire ROM commands +#define READROM 0x33 +#define SKIPROM 0xCC +;1-wire FUNCTION commands +#define CONVERTT 0x44 +#define READSCRATCHPAD 0xBE +;1-wire family codes for supported temperature sensors +#define DS_FAM_DS18S20 0x10 +#define DS_FAM_DS18B20 0x28 +;Return codes, controlling when to perform the next step +#define DONE 0 ;Next step after one second +#define CONTINUE 1 ;Next step as soon as possible + + extern float1, float2, flags, temp, StoreOutTemp + + UDATA +sensorstage res 1 +#define ds18b20 sensorstage,7 +#define restart sensorstage,6 +lsbstorage res 1 + + +#define OutsideTemp flags,0 ;Keep in sync with gateway.asm +#define OutsideInvalid flags,1 ;Keep in sync with gateway.asm + + global Temperature +Temperature CODE +Temperature skpz ;Zero bit indicates no message in prog + goto Restart ;Sequence interrupted by OT message + btfsc restart + clrf sensorstage + ;Split up the temperature measurement into several short steps, + ;each less than 1ms. That way the main loop is allowed to run + ;frequently enough for it to be able to do all the necessary + ;processing in time, like clearing out the serial receive + ;register. + movlw high StageTable + movwf PCLATH + movfw sensorstage + andlw b'11111' + addlw low StageTable + skpnc + incf PCLATH,F + incf sensorstage,F ;Determine next step of the process + movwf PCL ;Jump to the current step +StageTable goto PresencePulse ;Step 0 - Transmit a reset pulse + goto SkipRom ;Step 1 - Address all devices on the bus + goto Measure ;Step 2 - Start a measurement + retlw DONE ;Step 3 - Allow time for measurement + goto PresencePulse ;Step 4 - Transmit a reset pulse + goto ReadRom ;Step 5 - Send Read ROM command + goto ReadFamily ;Step 6 - Read device family code + goto PresencePulse ;Step 7 - Transmit a reset pulse + goto SkipRom ;Step 8 - Address all devices + goto ReadCommand ;Step 9 - Send Read Scratchpad command + goto ReadLSB ;Step 10 - Read temperature value, LSB + goto ReadMSB ;Step 11 - Read temperature value, MSB + goto Restart ;Step 12 - Start over (never reached) + +PresencePulse bcf IOPORT ;Prepare the output latch + bsf STATUS,RP0 + bcf IOPORT ;Drive the output port low + call Delay480 ;Wait for the minimum reset duration + bsf STATUS,RP0 + bsf IOPORT ;Release the I/O port + bcf STATUS,RP0 + call Delay60 ;Allow the device time to respond + btfsc IOPORT ;Check the 1-wire line + goto NoDevice + call Delay240 ;Wait for the presence pulse to end + retlw CONTINUE +NoDevice bsf OutsideInvalid ;Outside temperature is not valid + goto Restart ;Start the sequence from the top + +Measure movlw CONVERTT ;Initiate temperature conversion + call WriteByte ;Send the command on the 1-wire bus + retlw DONE ;Give devices time to do the conversion + +ReadFamily call ReadByte ;Read family code from the 1-wire bus + movfw temp + sublw DS_FAM_DS18S20 ;Check if the sensor is a DS18S20 + skpnz + retlw CONTINUE ;Found a DS18S20 + movfw temp + sublw DS_FAM_DS18B20 ;Check if the sensor is a DS18B20 + skpz + goto NoDevice ;Not a supported temperature sensor + bsf ds18b20 ;Found a DS18B20 + retlw CONTINUE + +ReadCommand movlw READSCRATCHPAD ;The scratchpad contains the temerature + goto WriteByte ;Send the command on the 1-wire bus + +ReadLSB call ReadByte ;Read the first scratchpad byte + movfw temp + movwf lsbstorage ;Save the byte for later use + retlw CONTINUE + +ReadMSB call ReadByte ;Read the second scratchpad byte + btfsc ds18b20 ;Check which sensor type was found + goto HighPrecision ;A DS18B20 stores the value differently + rrf temp,W ;Sign of the temperature value + rrf lsbstorage,W ;Determine the full degrees + movwf float1 + clrf float2 + rrf float2,F ;Determine the half degrees + goto Success +HighPrecision swapf lsbstorage,W ;Get the 1/16 degrees in the high nibble + andlw b'11110000' + movwf float2 ;Store as outside temperture low byte + swapf temp,W ;Get the upper nibble of the degrees + andlw b'11110000' + movwf temp ;Save for later use + swapf lsbstorage,W + andlw b'1111' ;Get the lower nibble of the degrees + iorwf temp,W ;Combine with the saved upper nibble + movwf float1 ;Store as outside temperature high byte +Success bsf OutsideTemp ;Temperature measurement completed + bsf OutsideInvalid ;Invalid until validity is checked + xorlw 85 ;Test against powerup value + skpnz + goto Restart ;Not a valid measurement + lcall StoreOutTemp ;Store the measurement + retlw CONTINUE + +Restart bsf restart ;Don't clear sensorstage for debugging + retlw DONE + +Delay480 call Delay240 ;480 = 2 * 240 +Delay240 movlw 240 - 8 ;Value for 240uS delay (compensated) + goto Delay ;Wait for the specified amount of time +Delay60 movlw 60 - 6 ;Value for 60uS delay (compensated) +Delay bsf STATUS,RP0 ;Bank 1 + bcf PIE1,TMR2IE ;Disable interrupt from timer 2 + subwf PR2,W ;Calculate the start value + bcf STATUS,RP0 ;Bank 0 + addlw 10 ;Overhead for starting and stopping + movwf TMR2 ;Load the timer + bsf T2CON,TMR2ON ;Start timer 2 +WaitTimer2 btfss PIR1,TMR2IF ;Check if timer 2 expired + goto WaitTimer2 ;Wait some more + bcf T2CON,TMR2ON ;Stop timer 2 + bcf PIR1,TMR2IF ;Prevent unwanted interrupt + return + +Delay15 call Delay5 ;15 = 3 * 5 +Delay10 call Delay5 ;10 = 2 * 5 +Delay5 nop ;Wait 1 us + return ;Subroutine Call + Return = 2us + 2us + +ReadByte call ReadNibble ;Read 4 bits from 1-wire bus +ReadNibble call ReadBit ;Read 1 bit from 1-wire bus + call ReadBit ;Read 1 bit from 1-wire bus + call ReadBit ;Read 1 bit from 1-wire bus +ReadBit bcf IOPORT ;Prepare output latch + bsf STATUS,RP0 ;TRIS register is in Bank 1 + bcf IOPORT ;Drive the 1-wire line low + clrc ;Prepare carry while waiting + bsf IOPORT ;Release the 1-wire bus + bcf STATUS,RP0 ;Back to bank 0 + call Delay10 ;Allow device time to write the bit + btfsc IOPORT ;Read the 1-wire bus + setc ;Device wrote a "1" + rrf temp,F ;Shift bit into the result varaible + movlw 45 ;Maximum time a device drives the bus + call Delay ;Wait for the device to release the bus + return + +ReadRom movlw READROM ;Command to read a device's ROM code + goto WriteByte ;Send the command on the 1-wire bus +SkipRom movlw SKIPROM ;Address all devices on the 1-wire bus +WriteByte movwf temp ;Temporarily store the 1-wire command + call WriteNibble ;Write 4 bits to the 1-wire bus +WriteNibble call WriteBit ;Write 1 bit to the 1-wire bus + call WriteBit ;Write 1 bit to the 1-wire bus + call WriteBit ;Write 1 bit to the 1-wire bus +WriteBit rrf temp,F ;Set carry to least significant bit +WriteSlot bcf IOPORT ;Prepare the output latch + bsf STATUS,RP0 + bcf IOPORT ;Drive the 1-wire line low + skpnc ;Carry contains the bit to send + bsf IOPORT ;Release the 1-wire line + movlw 60 - 10 ;Value for 60uS delay (compensated) + call Delay ;Bit duration + bsf STATUS,RP0 ;Bank 1 + bsf IOPORT ;Float the 1-wire line, if necessary + bcf STATUS,RP0 ;Bank 0 + retlw CONTINUE + + end diff --git a/boiler-monster/original-pic-4.2.5/gateway.asm b/boiler-monster/original-pic-4.2.5/gateway.asm new file mode 100644 index 0000000..da214b1 --- /dev/null +++ b/boiler-monster/original-pic-4.2.5/gateway.asm @@ -0,0 +1,4524 @@ + title "OpenTherm Gateway" + list p=16F88, b=8, r=dec + +;Copyright (c) 2009 Schelte Bron + +#define version "4.2" +#define phase "." ;a=alpha, b=beta, .=production +#define patch "5" ;Comment out when not applicable +;#define bugfix "1" ;Comment out when not applicable +#include build.asm + +;See the file "license.txt" for information on usage and redistribution of +;this file, and for a DISCLAIMER OF ALL WARRANTIES. + +#ifndef LVP + __config H'2007', B'10111101110100' +#else + __config H'2007', B'10111111110100' +#endif + __config H'2008', B'11111111111111' + + errorlevel -302 + +;The OpenTherm Gateway is located between the thermostat and the boiler of a +;central heating system. In monitor mode it passes its inputs unmodified to +;the outputs using only hardware functions. In back to back mode the messages +;from the thermostat are passed to the boiler under firmware control. In some +;cases the responses from the boiler may be overridden by the gateway and +;external data is injected into the messages. If no thermostat, or a simple +;on/off thermostat is connected, the gateway will generate its own opentherm +;messages to communicate with the Opentherm boiler. +; +;The gateway starts in back to back mode. It switches to monitor mode if for +;some reason the firmware locks up, resulting in a watchdog timeout. +; +;Interaction with the gateway takes place via a serial interface. The interface +;uses 9600 baud, 8 bits, no parity. +; +;Messages on the OpenTherm link are reported as a letter follwed by 8 hex +;digits. The letter indicates if the message was received from the boiler (B), +;or from the thermostat (T). If the gateway modifies the message to the boiler +;in any way, the modified request is also reported (R), as is a modified answer +;back to the thermostat (A). The hex digits represent the message contents. +; +;The serial interface also allows commands to be sent to the gateway. Commands +;consist of two letters, an equals-sign, and an argument. A command must be +;terminated by a carriage-return character (13, 0xd). The gateway understands +;the following commands: +;OT=+15.4 Specify the outside temperature +;TT=18.5 Change the temperature setpoint (temporary). Use 0 (+0.0) to +; return to the thermostat's normal programming. +;TC=+16.0 Change the temperature setpoint (continuously). Use +0.0 to +; return to the thermostat's normal programming. +;SB=15.5 Configure the setback temperature to use in combination with +; GPIO functions HOME (5) and AWAY (6). +;CS=10.0 Override the control setpoint from the thermostat +;VS=25 Override the ventilation setpoint from the thermostat +;MM=40 Override the maximum relative modulation from the thermostat +;SH=72.0 Set the maximum central heating setpoint. +;SW=60.0 Set the domestic hot water setpoint. +;SC=14:45/6 Set the clock and the day of the week, 1 = Monday, 7 = Sunday +;VR=3 Adjust the reference voltage between 0.6V and 2.5V +;HW=1 Control DHW. Use T or A to let the thermostat control DHW. +;LA=F Configure the function for LED A, defaults to Flame on +;LB=X Configure the function for LED B, defaults to Transmitting +;LC=O Configure the function for LED C, defaults to Setpoint Override +;LD=M Configure the function for LED D, defaults to Maintenance +;LE=P Configure the function for LED E, defaults to Raised Power +;LF=C Configure the function for LED F, defaults to Comfort Mode +; Possible function codes to use for the LEDs are: +; R: Receiving an Opentherm message from the thermostat or boiler +; X: Transmitting an Opentherm message to the thermostat or boiler +; T: Transmitting or receiving a message on the master interface +; B: Transmitting or receiving a message on the slave interface +; O: Remote setpoint override is active +; F: Flame is on +; H: Flame is on for central heating +; W: Flame is on for hot water +; C: Comfort mode (Domestic Hot Water Enable) is on +; E: Communication error has been detected +; M: Boiler requires maintenance +; P: Raised power mode +;GA=3 Configure the function for GPIO pin A, defaults to No Function +;GB=4 Configure the function for GPIO pin B, defaults to No Function +; Possible GPIO functions are: +; 0 (input): NONE: No GPIO function. +; 1 (output): GND: Port is constantly pulled low. +; 2 (output): VCC: Port is constantly pulled high. +; 3 (output): LEDE: Port outputs the LED E function. +; 4 (output): LEDF: Port outputs the LED F function. +; 5 (input): HOME: High - Cancel remote room setpoint override +; Low - Sets thermostat to setback temperature +; 6 (input): AWAY: High - Sets thermostat to setback temperature +; Low - Cancel remote room setpoint override +; 7 (in/out): DS1820: Drive DS18[SB]20 for outside temperature +;PR=L Print selected information. Supported information sources are: +; A: About opentherm gateway (prints the welcome message) +; B: Build date and time +; C: Clock speed of the PIC +; G: Report the GPIO pin configuration +; I: Report the GPIO pin input states +; L: Configured LED functions. This prints a string like FXOMPC +; M: Report the gateway mode +; O: Setpoint override +; P: Current Smart-Power mode. +; R: The state of the automatic Remeha thermostat detection. +; S: Print the configured setback temperature +; T: Tweak settings: Ignore Transition and Override in High Byte +; V: Report the reference voltage setting +; W: Domestic hot water setting +;PS=1 Select whether to only print a summary line (1) on request, or +; every received/transmitted opentherm message (0). +;AA=42 Add alternative message to send to the boiler instead of a +; message that is known not to be supported by the boiler. +;DA=42 Delete a single occurrence from the list of alternative messages +; to send to the boiler +;SR=18:1,230 Set the response for a DataID. Either one or two data bytes +; must be provided. +;CR=18 Clear the response for a DataID. +;UI=9 Tell the gateway which DataID's are not supported by the boiler, +; so the gateway can inject its own message. +;KI=9 Remove a DataID from the list of ID's that have been configured +; as unsupported by the boiler. +;RS=HBS Reset boiler counter. Supported counters are: +; HBS: Central heating burner starts +; HBH: Central heating burner operation hours +; HPS: Central heating pump starts +; HPH: Central heating pump operation hours +; WBS: Domestic hot water burner starts +; WBH: Domestic hot water burner operation hours +; WPS: Domestic hot water pump starts +; WPH: Domestic hot water pump operation hours +;GW=1 Switch between monitor (0) and gateway (1) mode +;IT=1 Control whether multiple mid-bit line transitions are ignored. +; Normally more than one such transition results in Error 01. +;OH=0 Copy the low byte of MsgID 100 also into the high byte to work +; with thermostats that look at the wrong byte (Honeywell Vision) +;FT=D Force thermostat model to Remeha Celcia 20 (C) or iSense (I). +;DP=1F Set the debug pointer to the specified file register + +;####################################################################### +; Peripheral use +;####################################################################### +;Comparator 1 is used for requests. The input is connected to the thermostat. +;In monitor mode, the output is connected to the boiler. +;Comparator 2 is used for responses. The input is connected to the Boiler. +;In monitor mode, the output is connected to the thermostat. + +;Timer 0 uses a 1:64 prescaler. It is used to measure the 10ms line idle-time +;and is the time base for lighting up the error LED for 2 seconds +;Timer 1 has a 1:8 prescaler, which gives it a period of 524288us, or roughly +;half a second. It is used for flashing the override LED. +;Timer 2 is used to create a 250uS time base. When the first low to high +;transition on one of the comparators is detected the timer is started with +;a 100us head start. A variable is used to count timer 2 roll-overs. Any +;transition of the comparator while the lowest two bits of the rollover +;counter are binary 10 marks a real bit. These bits are shifted into a 32 bit +;message buffer. Also timer 2 is resynchronized. When 34 bits have been +;received, the gateway will send a report to the serial interface. The report +;consists of either a 'T' or a 'B' and eight hex digits. The first letter +;indicates whether the message was received from the thermostat or the +;boiler. The hex digits represent the value of the 32-bits of data in the +;OpenTherm message. + +;The AUSART is configured for full duplex asynchronous serial communication at +;9600 baud, 8 bits, no parity. It is used for reporting about opentherm messages +;and receiving commands. + +;Analog input 0 is used to measure the voltage level on the opentherm line to +;the thermostat. This way the code can distinguish between a real opentherm +;thermostat and a simple on/off thermostat. An opentherm thermostat will keep +;the line at a few volts (low) or between 15 and 18 volts (high). An on/off +;thermostat will either short-circuit the line (0 volts) or leave the line open +;(20+ volts). + +#include "p16f88.inc" + +;Define the speed of the PIC clock +#define mhzstr "4" + +;BAUD contains the SPBRG value for 9600 baud + constant BAUD=25 + +;PERIOD is the value to be loaded into PR2 to obtain a 250uS period for TMR2 + constant PERIOD=249 + +;PRELOADT0 gives TMR0 a head start so the next rollover (with a 1:64 prescaler) +;will happen after 10 ms. This is the time the line must be stable to consider +;it idle after an error was detected. + constant PRELOADT0=100 + +;ONESEC contains the number of timer 0 overflows (with a 1:64 prescaler) +;that happen in roughly 1 second. This controls the frequency for generating +;Opentherm requests to the boiler when no Opentherm thermostat is connected. + constant ONESEC=61 ;61 * 64 * 256 = 999424 +;TWOSEC contains the number of timer 0 overflows (with a 1:64 prescaler) +;that happen in roughly 2 seconds. This is the time the error LED will stay +;lit after an error has been detected. + constant TWOSEC=2*ONESEC + +;Opentherm message types + constant T_READ=0x00 + constant T_WRITE=0x10 + constant T_INV=0x20 + constant B_RACK=0x40 + constant B_WACK=0x50 + constant B_INV=0x60 + constant B_UNKN=0x70 + +;Opentherm Data IDs + constant MSG_STATUS=0 + constant MSG_CTRLSETPT=1 + constant MSG_REMOTECMD=4 + constant MSG_FAULTCODE=5 + constant MSG_REMOTEPARAM=6 + constant MSG_SETPTOVERRIDE=9 + constant MSG_MAXMODULATION=14 + constant MSG_ROOMSETPOINT=16 + constant MSG_MODULATION=17 + constant MSG_CHPRESSURE=18 + constant MSG_DAYTIME=20 + constant MSG_BOILERTEMP=25 + constant MSG_OUTSIDETEMP=27 + constant MSG_RETURNTEMP=28 + constant MSG_DHWBOUNDS=48 + constant MSG_MAXCHBOUNDS=49 + constant MSG_DHWSETPOINT=56 + constant MSG_MAXCHSETPOINT=57 + constant MSG_FUNCOVERRIDE=100 + +;Opentherm remote requests + constant RCMD_TELEFOON=170 + +;Some default values to use if the boiler hasn't specified its actual limits. + constant MIN_DHWSETPOINT=20 + constant MAX_DHWSETPOINT=80 + constant MIN_CHSETPOINT=10 + constant MAX_CHSETPOINT=90 + +;Line voltage thresholds + constant V_SHORT=6 ;6 / 256 * 5 * 37.7 / 4.7 = 0.94V + constant V_LOW=58 ;58 / 256 * 5 * 37.7 / 4.7 = 9.09V + constant V_OPEN=122 ;122 / 256 * 5 * 37.7 / 4.7 = 19.11V + + extern SelfProg, Temperature + +;Variables accessible from all RAM banks + UDATA_SHR +s_wreg res 1 ;Used in interrupt routine +s_status res 1 ;Used in interrupt routine +s_fsr res 1 ;Used in interrupt routine + +temp res 1 +temp2 res 1 + +byte1 res 1 +byte2 res 1 +byte3 res 1 +byte4 res 1 +#define MsgParity byte1,7 +#define MsgResponse byte1,6 + +originaltype res 1 +originalreq res 1 +databyte1 res 1 +databyte2 res 1 +loopcounter res 1 + +mode res 1 +#define MonitorMode mode,0 ;The code expects this to be bit 0 +#define ChangeMode mode,1 +#define Overrun mode,2 +#define FailSafeMode mode,3 + +Bank0data UDATA 0x20 +rxbuffer res 16 ;Serial receive buffer + +;Variables for temporary storage +s_pclath res 1 ;Used in interrupt routine +tempvar0 res 1 +tempvar1 res 1 +tempvar2 res 1 +float1 res 1 +float2 res 1 + +;Variables for longer lasting storage +rxpointer res 1 +txpointer res 1 +bitcount res 1 ;Count of bits remaining in a message +celciasetpoint res 1 ;Setpoint times 5 as needed for a Celcia 20 +setpoint1 res 1 +setpoint2 res 1 +outside1 res 1 +outside2 res 1 +clock1 res 1 +clock2 res 1 +dhwsetpoint1 res 1 +dhwsetpoint2 res 1 +maxchsetpoint1 res 1 +maxchsetpoint2 res 1 +controlsetpt1 res 1 +controlsetpt2 res 1 +ventsetpoint res 1 +debug res 1 +outputmask res 1 +messagetype res 1 +alternativecmd res 1 +valueindex res 1 +txavailable res 1 +resetflags res 1 +prioritymsgid res 1 +dhwsetpointmin res 1 +dhwsetpointmax res 1 +chsetpointmin res 1 +chsetpointmax res 1 +minutetimer res 1 +#define ChangeTime minutetimer,7 + +SecCounter res 1 +MessagePtr res 1 +RemOverrideFunc res 1 +#define ManChangePrio RemOverrideFunc,0 +#define ProgChangePrio RemOverrideFunc,1 +#define TempChangePrio RemOverrideFunc,5 + +LineVoltage res 1 +MaxModLevel res 1 +GPIOFunction res 1 ;Funtions assigned to GPIO pins + +flags res 1 ;General bit flags +#define OutsideTemp flags,0 ;Keep in sync with ds1820.asm +#define OutsideInvalid flags,1 ;Keep in sync with ds1820.asm +#define SummaryRequest flags,2 +#define BoilerAlive flags,3 +#define BoilerFault flags,4 +#define NegativeTemp flags,6 +#define LeadingZero flags,7 + +stateflags res 1 ;More bit flags +#define MessageRcvd stateflags,0 +#define Unsolicited stateflags,1 +#define HideReports stateflags,2 +#define CommandComplete stateflags,3 +#define NoAlternative stateflags,4 +#define AlternativeUsed stateflags,5 +#define OverrideUsed stateflags,6 +#define BoilerResponse stateflags,7 + +msgflags res 1 ;Used in interrupt routine +#define NextBit msgflags,0 +#define Transition msgflags,1 +#define Comparator1 msgflags,2 ;Must be bit 2 +#define Comparator2 msgflags,3 ;Must be bit 3 +#define Transmit msgflags,4 +#define Intermission msgflags,5 +#define MidBit msgflags,6 +#define Parity msgflags,7 +#define NextBitMask 1 << 0 + +quarter res 1 ;Count of 1/4 ms, used in interrupt routine +#define LineStable quarter,0 +#define RealBit quarter,1 +#define BitClkCarry quarter,3 + +tstatflags res 1 ;Used in- and outside interrupt routine +#define SmartPower tstatflags,7 ;Thermostat supports Smart Power +#define DisconnectEvent tstatflags,5 ;Long high without Smart Power +#define OneSecond tstatflags,4 ;Time to send the next message +#define Request tstatflags,3 ;Event on the thermostat line +#define PowerReport tstatflags,2 +#define HighPower tstatflags,1 +#define RaisedPower tstatflags,0 + +override res 1 +#define OverrideBlink override,7 ;Keep track of blinking LED +#define OverrideWait override,6 ;Override has not yet been sent +#define OverrideFunc override,5 ;ID100 has not yet been sent +#define OverrideClr override,4 ;Cancel override request +#define OverrideReq override,3 ;Override temp was requested +#define OverrideAct override,2 ;Override accepted by thermostat +;Bits 0&1 are used as a counter to allow 3 attempts for the override to stick + +initflags res 1 +#define InitParameter initflags,0 +#define InitHotWater initflags,1 +#define InitHeating initflags,2 +#define WaterSetpoint initflags,3 +#define MaxHeatSetpoint initflags,4 +#define NoResetUnknown initflags,6 +#define PriorityMsg initflags,7 + +onoffflags res 1 +#define dhwupdate onoffflags,0 ;Must be bit 0 +#define maxchupdate onoffflags,1 ;Must be bit 1 +#define NoThermostat onoffflags,2 +#define HeatDemand onoffflags,3 +#define HotWaterSwitch onoffflags,4 +#define HotWaterEnable onoffflags,5 +#define UserMaxModLevel onoffflags,6 +#define CHModeOff onoffflags,7 + +gpioflags res 1 +#define gpioaway gpioflags,0 +#define VirtualLedE gpioflags,1 +#define VirtualLedF gpioflags,2 +#define gpio_port1 gpioflags,6 +#define gpio_port2 gpioflags,7 +#define gpio_mask gpioflags + +;Flags to work around quirks of remeha thermostats +remehaflags res 1 +#define TStatRemeha remehaflags,7 ;Auto-detected Remeha Thermostat +#define TStatISense remehaflags,6 ;Thermostat is Remeha iSense +#define TStatCelcia remehaflags,5 ;Thermostat is Remeha Celcia 20 +#define TStatManual remehaflags,4 ;Thermostat model set manually + +s_timer2 res 1 ;Store timer 2 value, used in interrupt routine + +errornum res 1 +errortimer res 1 + +repeatcount res 1 ;Count same message from the thermostat + +TSPCount res 1 +TSPIndex res 1 + +;If the serial receive pin is disconnected and it picks up some 50Hz noise, the +;AUSART would detect a BREAK every 20ms. To prevent the gateway from repeatedly +;resetting in this situation, wait for 32 TMR0 overflows (slightly over 0.5s) +;without a BREAK before acting on a BREAK again. +BreakTimer res 1 +#define NoBreak BreakTimer,5 ;Set after 32 TMR0 overflows + +settings res 1 ;Bits 0-4: voltage reference +#define IgnoreErr1 settings,5 ;For bouncing high<->low changes +#define OverrideHigh settings,6 ;Copy MsgID100 data to high byte + +resetreason res 1 +boilercom res 1 + + global float1, float2, flags, temp, StoreOutTemp + +Bank1data UDATA 0xA0 +;The functions that can be assigned to a LED are referred to by an uppercase +;letter. One byte is reserved for each possible function. Bits 3, 4, 6, and 7 +;indicate if the function is assigned to one or more of the four LED outputs. +;Bit 0 is used to keep track of the current state of the function. That makes +;it possible to immediately set a LED to the correct state upon assignment of +;the function. +functions res 26 + +;The gateway keeps track of which messages are not supported by the boiler. +;The code counts the times the boiler responds with an Unknown-DataID in two +;bits per DataID. If the counter for a specific DataID reaches 3, the gateway +;stops sending the DataID to the boiler. The next time the thermostat requests +;that information again, the gateway will actually send a different request to +;the boiler. This allows gathering of information that the thermostat does not +;normally request. +unknownmap res 32 ;2 bits per DataID for 128 DataID's + +; 6 bytes remaining in Bank 1 + +Bank1values UDATA 0xE0 +ResponseValues1 res 16 ;DataID's of fake responses + + +Bank2data UDATA 0x110 +;Keep track of the data values of up to 40 DataID's +valuestorage res 80 ;2 bytes per DataID for up to 40 DataID's +Bank2values UDATA 0x160 +ResponseValues2 res 16 ;High byte of fake responses + +Bank3data UDATA 0x190 +;Use all available RAM in bank 3 for the transmit buffer + constant TXBUFSIZE=80 +txbuffer res TXBUFSIZE +Bank3values UDATA 0x1E0 +ResponseValues3 res 16 ;Low byte of fake responses + +;I/O map +#define MasterIn CMCON,C1OUT +#define SlaveIn CMCON,C2OUT +#define SlaveOut PORTA,3 +#define MasterOut PORTA,4 +#define RXD PORTB,2 +#define TXD PORTB,5 +#define CCP1 PORTB,0 +#define LED_A PORTB,3 +#define LED_B PORTB,4 +#define LED_C PORTB,6 +#define LED_D PORTB,7 +#define RSET PORTB,1 ;Used by self-programming + +#define SlaveMask b'00001000' +#define MasterMask b'00010000' + +package macro pkg +pkg code +pkg + endm + +pcall macro rtn + lcall rtn + pagesel $ + endm + +;Skip the next instruction (bit 7 of PCLATH is always 0) +skip macro + btfsc PCLATH,7 + endm + +;Get the output of the active comparator into the carry bit +getcompout macro + bsf STATUS,RP0 + rlf CMCON,W ;Get the output of comparator 2 + bcf STATUS,RP0 + btfsc Request ;A request goes through comparator 1 + addlw b'10000000' ;Cause a carry if C1OUT is high + endm + +;The first thing to do upon a reset is to allow the firmware to be updated. +;So no matter how buggy freshly loaded firmware is, if the first two command +;have not been messed up, the device can always be recovered without the need +;for a PIC programmer. The self-programming code times out and returns after +;a second when no firmware update is performed. +; + +ResetVector code 0x0000 + lcall SelfProg ;Always allow a firmware update on boot + lgoto Start ;Start the opentherm gateway program + +;Handle interrupts. This piece of code saves the important registers and finds +;out which interrupt fired. It then calls a subroutine to handle that specific +;interrupt. Upon return from the subroutine, it restores the saved registers. +; +InterruptVector code 0x0004 + movwf s_wreg ;Save the work register + swapf STATUS,W ;Don't change any status flags + clrf STATUS ;Start with a known state + movwf s_status ;Save the (swapped) status register + movfw FSR + movwf s_fsr ;Save the FSR register + movfw PCLATH + movwf s_pclath + pagesel Interrupt + movfw TMR2 + movwf s_timer2 ;Save the current value of timer2 + ;Process the timer 2 interrupt before the comparator + ;interrupt to get the correct overflow count + btfsc PIR1,TMR2IF ;Check if timer 2 matched + call timer2int + btfsc PIR2,CMIF ;Check if an input changed + call inputint + ;Restore the registers and return from the interrupt + movfw s_pclath + movwf PCLATH + movfw s_fsr ;Get the old value of the FSR register + movwf FSR ;Restore the indirect addressing pointer + swapf s_status,W ;Swap the saved status register back + movwf STATUS ;Restore the status register + swapf s_wreg,F ;Don't change any status flags + swapf s_wreg,W ;Restore the work register + retfie ;End of interrupt routine + +;######################################################################## +;Interrupt routines +;######################################################################## +; +;The meaning of events on the opentherm interface depends on the logical levels +;and timing. The state is tracked via a number of bits and variables. +; +;TMR2ON==0 && CXOUT==1: Line idle +;TMR2ON==0 && CXOUT:=0: Start T2 +;bitcount==0 && quarter<=3 && CXOUT:=1: Start of opentherm message. Restart T2 +;bitcount>0 && quarter<=3 && CXOUT:=0: Midbit transition, ignore. +;bitcount>0 && quarter<=3 && CXOUT:=1: Midbit transition, ignore. +;bitcount>0 && quarter>3 && quarter<=6 && CXOUT:=0: Bit: 0. Restart T2 +;bitcount>0 && quarter>3 && quarter<=6 && CXOUT:=1: Bit: 1. Restart T2 +;quarter:=20 && C1OUT==0 && C1INV==0: Smartpower on. +;quarter>20 && quarter<80 && + + package Interrupt + +;Comparator interrupt routine +inputint bsf STATUS,RP0 + swapf CMCON,W ;Read to end the mismatch condition + bcf STATUS,RP0 + bcf PIR2,CMIF ;Clear the interrupt + xorwf msgflags,W ;Determine which comparator has changed + andlw b'00001100' ;Filter out the comparator bits + skpnz ;Make sure there actually was a change + return ;False trigger + xorwf msgflags,F ;Update the saved state + andlw b'00000100' ;Check for a change from the thermostat + skpnz ;Thermostat wants attention + btfss Request ;Or not receiving a request + goto inputmark ;then continue + movlw -1 ;Suppress errors for a while + movwf errornum + return + +;The thermostat may try to change power levels while the gateway is in the +;process of transmitting a message, or receiving a message from the boiler. +;The same timer is used for all opentherm communication, so the gateway can't +;handle the two things at the same time. Ignoring the thermostat may result in +;incorrect power levels leading to user-visible artefacts (like a flashing +;backlight of the thermostat screen). So, for a smoother user experience, we +;abort the current operation when the thermostat asks for attention. +inputmark skpz ;Z bit is cleared if comparator1 changed + btfsc Request ;Already listening to the thermostat + goto inputboiler ;Not a new event from the thermostat + bsf Request ;Receiving from the thermostat + tstf bitcount ;Message in progress? + skpnz + btfsc Transmit ;Transmission in progress? + bcf Request + goto activity ;End of the interrupt routine +inputboiler tstf bitcount ;Is a message in progress? + skpnz + goto InputEvent ;Line state change outside message + movlw 3 ;3 overflows of timer 2 = 750 us + subwf quarter,W ;Check the time since the last bit + skpc ;More than 750 us + goto midbitchange + getcompout ;Get the comparator out + decfsz bitcount,F ;Is this the stop bit? + goto storebit ;If not, store the bit in the buffer + skpc ;Carry is set when the line is low + goto error02 ;A stop bit must be logical 1 + btfsc Parity ;Check for even parity + goto error04 ;Parity error + bsf MessageRcvd ;Indicate that a message was received + pagesel Message + movlw 'R' ;Receive function + call SwitchOffLED ;Switch off the receive LED + movlw 'T' ;Thermostat function + call SwitchOffLED ;Switch off the thermostat LED + movlw 'B' ;Boiler function + call SwitchOffLED ;Switch off the boiler LED + pagesel Interrupt + bcf Request ;Thermostat is done +inputspike bcf T2CON,TMR2ON ;Stop timer 2 + goto activity ;End of the interrupt routine + +inputabort clrf bitcount ;Abort message + bcf Transmit ;Clear flag + movlw b'00011000' ;Default state of the outputs + btfsc RaisedPower + movlw b'00001000' ;Idle state of port 4 is high + xorwf PORTA,W ;Check against the current state + andlw b'00011000' ;Only consider port 3 and 4 + xorwf PORTA,F ;Update the ports + pagesel Message + movlw 'X' + call SwitchOffLED ;Switch off transmit LED + movlw 'B' + call SwitchOffLED ;Switch off boiler LED + pagesel Interrupt + goto restarttimer ;Reset T2 and 1/4 ms counter + +storebit ;Store the current bit in the message buffer + movlw 1 << 7 ;Mask for the bit flag tracking parity + skpnc + xorwf msgflags,F ;Update the parity + rlf byte4,F ;Shift the 32 bits receive buffer + rlf byte3,F ;Shift bits 8 through 15 + rlf byte2,F ;Shift bits 16 through 23 + rlf byte1,F ;Shift bits 24 through 31 + bcf MidBit ;Mid-bit transitions are allowed again +restarttimer clrf quarter ;Restart 1/4 ms counter + movfw s_timer2 ;Timer 2 value just after the interrupt + subwf TMR2,F ;Adjust the timer value + movlw 6 ;Difference between 255 and PR2 + skpc + subwf TMR2,F ;Compensate for early reset of T2 + bcf PIR1,TMR2IF ;Interrupt routine is less than 250us. +activity movlw PRELOADT0 ;Idle time preload value + movwf TMR0 ;Restart idle timer + return ;End of the interrupt routine + +InputEvent ;The line state changed while not receiving a message. Need to + ;figure out what is going on. + btfsc T2CON,TMR2ON ;Timer2 is stopped when the line is idle + goto InputProcess + ;If an unreported error has happened, we're still waiting for + ;the line to become idle again. + tstf errornum ;Is there an unreported error? + skpz + goto activity ;Restart the non-idle timer + ;Change of a previously idle line + movlw 42 ;Time since start of interrupt routine + movwf TMR2 ;Initialize timer 2 + bsf T2CON,TMR2ON ;Start timer2 + clrf quarter ;Reset the 1/4 ms counter + bsf STATUS,RP0 + bsf PIE1,TMR2IE ;Enable timer2 interrupts + bcf STATUS,RP0 + goto activity ;The change has no meaning + +InputProcess ;A second line change happened shortly after the previous one. + movlw 10 + subwf quarter,W + skpnc ;Less than 2.5 ms since the last change + goto restarttimer +InputFast getcompout ;Get the comparator output + skpc ;Carry is set when the line is low + goto activity ;Not a start bit + bsf STATUS,RP0 + btfss CMCON,C1INV ;Carry is guaranteed to be set here + clrc ;Clear carry if not inverting + bcf STATUS,RP0 + skpnc ;Thermostat line not inverted? + btfsc HighPower ;High power mode not yet established? + goto StartBit + bsf HighPower ;Start of message on inverted line + bsf PowerReport ;Report high power mode +StartBit tstf quarter ;Check if pulse was less than 250us + skpnz + goto inputspike ;Ignore spikes + pagesel Message + movlw 'R' ;Receive function + call SwitchOnLED ;Turn on the receive LED + movlw 'B' ;Boiler function + btfsc Request + movlw 'T' ;Thermostat function + call SwitchOnLED ;Switch on the boiler or thermostat LED + pagesel Interrupt + movlw 33 ;Message length: 32 bits + stop bit - 1 + movwf bitcount ;Initialize the counter + bcf Parity ;Initialize the parity check flag + bcf MidBit ;No mid-bit transitions yet + goto restarttimer ;Significant transition of the start bit + +;Timer 2 interrupt routine + +timer2int bcf PIR1,TMR2IF ;Clear the interrupt flag + incf quarter,F ;Update the 1/4 ms counter + btfsc Transmit ;Transmitting a message? + goto timer2xmit + tstf bitcount ;Message in progress? + skpnz + goto timer2idle + btfsc BitClkCarry ;Bit transition happened on time? + goto error03 ;Report missing transition + return + +timer2xmit btfsc LineStable ;Nothing to do in a stable interval + return + tstf bitcount + skpnz + goto timer2xmitend + clrw ;Initialize work register + btfss NextBit ;Find out the desired output level + movlw b'00011000' ;Set bits matching the opentherm outputs + btfsc RaisedPower ;Using raised power to the thermostat? + xorlw b'00010000' ;Invert the bit for the thermostat line + xorwf PORTA,W ;Compare with the current port levels + andwf outputmask,W ;Only apply for the relevant output + xorwf PORTA,F ;Update the output port + btfsc RealBit ;Check if this was the start of a bit + goto InvertBit ;Next time send the inverted value + bcf NextBit ;Start with assumption next bit is 0 + setc ;Prepare to shift in a stop bit + rlf byte4,F ;Shift the full 32 bit message buffer + rlf byte3,F ;Shift bits 8 through 15 + rlf byte2,F ;Shift bits 16 through 23 + rlf byte1,F ;Shift bits 24 through 31 + skpnc ;Test the bit shifted out of the buffer + bsf NextBit ;Next time, send a logical 1 + clrf quarter + decf bitcount,F ;Test for the end of the message + return ;Return from the interrupt routine + +timer2xmitend bcf T2CON,TMR2ON ;Stop timer 2 + bcf Transmit ;Finished transmitting + btfsc outputmask,4 ;Transmission to the thermostat? + bsf Intermission ;Message exchange is finished + pagesel Message + movlw 'X' ;Transmit function + call SwitchOffLED ;Switch off the transmit LED + movlw 'T' ;Thermostat function + call SwitchOffLED ;Switch off the thermostat LED + movlw 'B' ;Boiler function + call SwitchOffLED ;Switch off the boiler LED + pagesel Interrupt + return ;Return from the interrupt routine + +InvertBit movlw NextBitMask ;Load a mask for the NextBit flag + xorwf msgflags,F ;Invert the bit + return ;Return from the interrupt routine + +timer2idle btfss Request ;Event was on the thermostat line? + return ;Other events only apply to thermostat + movfw quarter + sublw 20 ;New state persisted for 5ms? + skpnz + goto timer2invert + movlw 80 + subwf quarter,W ;20ms since last line event? + skpc + return + bcf T2CON,TMR2ON ;No line event for 20ms + bcf Request ;Thermostat is done + bsf STATUS,RP0 + btfss CMCON,C1INV ;Carry is guaranteed to be set here + clrc ;Clear carry if not inverting + bcf STATUS,RP0 + skpc ;Thermostat line inverted? + return + bsf HighPower ;low/medium => high power transition + bsf PowerReport ;Power levels have changed + return + +timer2invert btfsc Comparator1 ;Line high (according to current mode)? + return ;Not interresting + btfss SmartPower ;Thermostat supports Smart Power? + goto ExtendedHigh ;Long high level without Smart Power + bsf STATUS,RP0 ;Switch to bank 1 + movlw 1 << C1INV ;Bit mask for the comparator1 invert bit + xorwf CMCON,F ;Toggle the invert bit + btfss CMCON,C1INV ;Carry is guaranteed to be set here + clrc ;Clear carry if not inverting + swapf CMCON,W ;Read the new comparator states + bcf STATUS,RP0 + bcf PIR2,CMIF ;Clear the interrupt caused by inverting + xorwf msgflags,W ;Get the saved comparator state + andlw b'00001100' ;Filter out the comparator bits + ;Always expect W to be 0x04 here. + xorwf msgflags,F ;Update the saved state + skpnc ;Inverting + goto SmartPowerOn + bsf PowerReport ;Power levels have changed + btfss HighPower + return ;Medium power + bcf RaisedPower ;High/Medium => low transition + bcf HighPower ;Clear bit for next round + bsf MasterOut ;Make the thermostat line low + movlw 'P' + pcall SwitchOffLED ;Switch off the raised power mode LED + return +ExtendedHigh bsf DisconnectEvent ;Simulated disconnect + return +SmartPowerOn btfsc RaisedPower ;Already in raised power mode? + goto MaximumPower + bsf RaisedPower ;In raised power mode + bcf MasterOut ;Make the thermostat line high + movlw 'P' + pcall SwitchOnLED ;Switch on the raised power mode LED + return +MaximumPower bsf HighPower ;This will produce the desired behavior + return + +midbitfirst bsf MidBit ;Remember a mid-bit transition happened + goto activity +midbitchange btfsc MidBit ;First mid-bit transition? + btfsc IgnoreErr1 ;Or multiple transitions are ignored? + goto midbitfirst ;Register mid-bit transition + +;Error 01: More than one mid-bit transition +error01 movlw 1 + goto errorcleanup + +;Error 02: Wrong stop bit +error02 movlw 2 + goto errorcleanup + +;Error 03: Bit not received in time +error03 movlw 3 + goto errorcleanup + +;Error 04: Parity error +error04 movlw 4 + +errorcleanup tstf errornum + skpnz + movwf errornum + bcf T2CON,TMR2ON ;Stop timer 2 + clrf bitcount + clrf quarter + bcf Transmit + pagesel Message + movlw 'X' + call SwitchOffLED ;Switch off the transmit LED + movlw 'R' + call SwitchOffLED ;Switch off the receive LED + movlw 'B' + call SwitchOffLED ;Switch off the boiler LED + movlw 'T' + call SwitchOffLED ;Switch off the thermostat LED + comf errornum,W + skpnz + goto errorskip ;Not a real error + movlw 'E' + call SwitchOnLED ;Switch on the error LED + movlw TWOSEC ;Leave the error LED on for 2 seconds + movwf errortimer +errorskip lgoto activity ;Wait for the line to go idle + +;######################################################################## +; Main program +;######################################################################## + + package Main +Break tstf RCREG ;Clear the RCIF interrupt flag + btfsc NoBreak + goto Restart2 + clrf BreakTimer ;Reset break-free timer + goto MainLoop ;continue running the mainloop +Restart1 bsf resetreason,0 ;Reset because we're stuck in a loop +Restart2 bsf resetreason,1 ;Reset due to BREAK on serial line + pcall SelfProg ;Jump to the self-programming code +Start bcf STATUS,RP1 + bcf STATUS,RP0 + clrf PORTB ;Flash the LED's on startup + bsf STATUS,RP0 + movlw b'01100000' ;Internal oscillator set to 4MHz + movwf OSCCON ;Configure the oscillator + + ;Configure I/O pins + ;Power on defaults all pins to inputs + ;Port A + ;Pins 0 and 1 are used as comparator inputs + ;Pin 2 is used as VREF (must be configured as input!) + ;Pins 3 and 4 are (comparator) ouputs + ;Pin 5 is master reset input + ;Pins 6 and 7 are GPIO + movlw b'11100111' + movwf TRISA + + ;Port B + ;Pins 2 and 5 are used by the USART and don't need to be configured + ;Pins 3, 4, 6, and 7 are used to indicate events for debugging +#ifndef LVP + movlw b'00100111' +#else + ;Can't use RB3 if LVP is enabled + movlw b'00101111' +#endif + movwf TRISB + + ;Check for reset reason + movlw b'11' + xorwf PCON,W + movlw 4 << 4 + btfsc PCON,NOT_POR + movlw 5 << 4 + bcf STATUS,RP0 + skpnz + swapf resetreason,W + movwf mode + btfss STATUS,NOT_TO + bsf FailSafeMode + bsf STATUS,RP0 + movlw b'11' + movwf PCON + + ;Configure Timer 0 & Watchdog Timer + ;Bit 7 RBPU = 1 - Port B pull up disabled + ;Bit 6 INTEDG = 1 - Interrupt on rising edge + ;Bit 5 T0CS = 0 - Internal clock + ;Bit 4 T0SE = 1 - High to low transition + ;Bit 3 PSA = 0 - Prescaler assigned to Timer 0 + ;Bit 2-0 PS = 5 - 1:64 Prescaler + movlw b'11010101' + movwf OPTION_REG + + ;Configure Timer 2 + ;Generate an interrupt every 250uS (one fourth of a bit) + ;Bit 6-3 TOUTPS = 0 - 1:1 Postscaler + ;Bit 2 TMR2ON = 0 - Timer disabled + ;Bit 1-0 T2CKPS = 0 - 1:1 Prescaler + movlw PERIOD ;Fosc / 16000 - 1 + movwf PR2 + bcf STATUS,RP0 +#ifndef T2PRESCALER + movlw b'00000000' +#else + movlw b'00000001' +#endif + movwf T2CON + + clrf TMR1L + clrf TMR1H + ;Configure Timer 1 + ;Used to blink LEDs approximately once per second + ;Bit 5-4 T1CKP = 3 - 1:8 Prescale value + ;Bit 3 T1OSCEN = 0 - Oscillator is shut off + ;Bit 2 T1SYNC = 0 - Bit is ignored because TMR1CS=0 + ;Bit 1 TMR1CS = 0 - Internal clock (Fosc/4) + ;Bit 0 TMR1ON = 1 - Enable Timer1 + movlw b'110001' + movwf T1CON + + ;Delay a few milliseconds to show the lit LEDs + clrf TMR0 + bcf INTCON,TMR0IF +WaitTimer0 clrwdt + btfss INTCON,TMR0IF + goto WaitTimer0 + + ;Configure comparator module + ;Bit 7 C2OUT = R/O + ;Bit 6 C1OUT = R/O + ;Bit 5 C2INV = 0 - Not inverted + ;Bit 4 C1INV = 0 - Not inverted + ;Bit 3 CIS = 0 - Irrelevant + ;Bit 2-0 = 3 - Common reference / 6 - with outputs + bsf STATUS,RP0 + movlw b'00000011' ;Common reference mode + btfsc FailSafeMode + movlw b'00000110' ;Common reference with outputs + movwf CMCON + movlw b'00000111' ;A0 through A2 are used for analog I/O + movwf ANSEL + + ;Configure the serial interface + movlw BAUD + movwf SPBRG + bsf TXSTA,BRGH ;9600 baud + bcf TXSTA,SYNC ;Asynchronous + bsf TXSTA,TXEN ;Enable transmit + bcf STATUS,RP0 + bsf RCSTA,SPEN ;Enable serial port + btfss FailSafeMode + bsf RCSTA,CREN ;Enable receive + + ;Configure A/D converter + movlw b'01000001' ;A/D on, channel 0, Fosc/8 + movwf ADCON0 + + ;Clear RAM in bank 0 & 1 + movlw 0x20 + movwf FSR + movlw 80 + movwf loopcounter + movlw 0x80 +InitLoop0 clrf INDF + xorwf FSR,F + clrf INDF + incf FSR,F + decfsz loopcounter,F + goto InitLoop0 + ;Clear RAM in bank 2 + movlw low 0x110 + bsf STATUS,IRP + movwf FSR + movlw 96 + movwf loopcounter +InitLoop1 clrf INDF + incf FSR,F + decfsz loopcounter,F + goto InitLoop1 + + ;Configure the voltage reference module + ;The reference voltage must be set to 1.3V + ;Bit 7 VREN = 1 - VREF Enabled + ;Bit 6 VROE = 1 - Output on pin RA2 + ;Bit 5 VRR = 1 - Low range + ;Bit 3-0 VR = 6 - 1.25V (7 - 1.46V) + movlw SavedSettings + call ReadEpromData ;Get the setting from EEPROM + movwf settings ;Store a copy in RAM + iorlw b'11100000' + bsf STATUS,RP0 + movwf CVRCON ;Set the reference voltage + + ;Setup interrupt sources + bsf PIE2,CMIE ;Enable comparator interrupt + bsf PIE1,TMR2IE ;Enable timer 2 interrupts + + swapf CMCON,W + clrf STATUS + bcf PIR2,CMIF ;Clear any comparator interrupt + andlw b'00001100' + movwf msgflags + rlf msgflags,W + iorlw b'11100111' + movwf PORTA + movlw b'11111111' + movwf PORTB + bsf INTCON,PEIE + btfss FailSafeMode + bsf INTCON,GIE + comf alternativecmd,F;Compensate for initial increment + movlw b'111' + movwf initflags ;Nothing has been initialized yet + movlw MIN_DHWSETPOINT + movwf dhwsetpointmin + movlw MAX_DHWSETPOINT + movwf dhwsetpointmax + movlw MIN_CHSETPOINT + movwf chsetpointmin + movlw MAX_CHSETPOINT + movwf chsetpointmax + + movlw 100 + movwf MaxModLevel ;Initialize Max Modulation Level at 100% + + movlw ONESEC + movwf SecCounter ;Initialize second counter + + movlw TStatModel + call ReadEpromData + movwf remehaflags + + ;Reinstall manually configured unsupported MsgID's + movlw 16 ;Size of flag data: 16 * 8 = 128 msgs + movwf loopcounter ;Initialize loop counter + clrf float1 ;Message number + movlw UnknownFlags ;Start of bitmap in data EEPROM + movwf tempvar0 ;Pointer to byte of bitmap +UnknownLoop1 movfw tempvar0 ;Get the bitmap pointer + call ReadEpromData ;Read the flags for 8 MsgID's + movwf float2 ;Temporary storage of bit flags + movfw float1 ;Number of the first of 8 MsgID's +UnknownLoop2 tstf float2 ;Check if any remaining flag is set + skpnz + goto UnknownJump2 ;Skip over the rest + movwf float1 ;Update the message number + clrc + rrf float2,F ;Shift a flag into the carry bit + skpc + goto UnknownJump1 ;Current message is not flagged + pcall UnknownMask2 ;Get mask and pointer into unknownmap + iorwf INDF,F ;Mark message as unsupported +UnknownJump1 incf float1,W ;Next message + goto UnknownLoop2 ;Repeat the loop +UnknownJump2 movlw b'111' + iorwf float1,F ;The last of the current 8 MsgID's + incf float1,F ;The first of the next 8 MsgID's + incf tempvar0,F ;Also advance the bitmap pointer + decfsz loopcounter,F + goto UnknownLoop1 ;Repeat the loop + + movlw GreetingStr + pcall PrintStringNL + + ;Initialize the LED functions + clrf temp + movlw FunctionLED1 + call ReadEpromData + pcall SetLEDFunction + incf temp,F + movlw FunctionLED2 + call ReadEpromData + pcall SetLEDFunction + incf temp,F + movlw FunctionLED3 + call ReadEpromData + pcall SetLEDFunction + incf temp,F + movlw FunctionLED4 + call ReadEpromData + pcall SetLEDFunction + incf temp,F + movlw FunctionLED5 + call ReadEpromData + pcall SetLEDFunction + incf temp,F + movlw FunctionLED6 + call ReadEpromData + pcall SetLEDFunction + + ;Initialize the GPIO ports + movlw FunctionGPIO + call ReadEpromData + movwf GPIOFunction + pcall gpio_init + + btfss FailSafeMode + goto MainLoop + movlw 'M' + lcall SwitchOnLED + movlw TimeoutStr + pcall PrintStringNL + +;************************************************************************ + +MainLoop clrwdt + + tstf txavailable ;Check if there is data to transmit + skpz + call ProcessOutput + + btfsc FailSafeMode + goto MainLoop + + ;Check if an OpenTherm message was received + btfsc MessageRcvd + call OpenThermMsg + + btfsc PowerReport + call ReportPower ;Report a power level change + + btfsc INTCON,TMR0IF + call IdleTimer + + btfsc PIR1,TMR1IF + call FlashTimer + + btfsc PIR1,ADIF + call CheckThermostat + + pcall gpio_common + + btfsc SummaryRequest ;Summary report requested/in progress? + call SummaryReport + + btfsc repeatcount,6 ;Reset after same message 64 times + goto Restart1 + + btfss PIR1,RCIF ;Activity on the serial receiver? + goto MainLoop + + ;FERR is only relevant if RCIF is set + btfsc RCSTA,FERR ;Check for framing error (break) + goto Break + + call SerialEvent + goto MainLoop + +;Callers depend on this function never setting the carry bit +SerialReceive movfw rxpointer + addlw rxbuffer + movwf FSR + movfw RCREG + btfsc CommandComplete + return ;Not ready to accept a command now + movwf INDF + xorlw 0x0a ;Ignore linefeed characters + skpnz + return + xorlw 0x0a ^ 0x0d ;Check for end of command + skpnz + goto SerialCmdEnd + movfw rxpointer + addlw 1 + skpdc ;All commands are less than 15 chars + movwf rxpointer + return ;User is still typing the command +SerialCmdEnd tstf rxpointer ;Check command length + skpnz + return ;Ignore empty commands + clrf INDF ;Indicate end of command + bsf CommandComplete + return + +SerialEvent btfss RCSTA,OERR ;Check for overrun error + goto SerialEventJmp1 + bcf RCSTA,CREN ;Procedure to clear an overrun error + bsf RCSTA,CREN ;Re-enable the serial interface + bsf Overrun ;Remember for later reporting +SerialEventJmp1 call SerialReceive + btfss CommandComplete + return + lcall SerialCommand + clrf rxpointer ;Prepare for a new command + bcf CommandComplete + pagesel Print + iorlw 0 + skpz + call PrintString ;Print the result + call NewLine + pagesel $ + goto PrtDebugPointer + +OpenThermMsg pcall PrintRcvdMsg ;Report the received message + bcf MessageRcvd ;Message has been reported + btfsc MonitorMode ;In monitor mode there is no need ... + goto MonModeCommOK ;... to explicitly transmit the message + pcall HandleMessage ;Apply special treatment + btfss NoThermostat + goto SendMessage + bsf Intermission ;Message exchange is finished + movfw byte2 + xorwf originalreq,W + skpz + goto PrtDebugPointer ;Didn't get the expected answer + incf MessagePtr,F + bcf MessagePtr,4 +MonModeCommOK clrf repeatcount ;Communication is working + goto PrtDebugPointer ;No need to send the message +SendMessage movlw 1 + movwf quarter ;Initialize the state counter + bsf NextBit ;Start bit is 1 + bsf Transmit ;Starting to transmit a message + movlw 'X' ;Transmit function + pagesel Message + call SwitchOnLED ;Switch on the transmit LED + movlw 'B' ;Boiler function + btfsc MsgResponse + movlw 'T' ;Thermostat function + ; In package Message + call SwitchOnLED ;Switch on the boiler or thermostat LED + movlw SlaveMask ;Transmitting to the boiler + btfsc MsgResponse ;Check message direction + movlw MasterMask ;Transmitting to the thermostat + movwf outputmask + movlw 34 ;A message is 32 bits + start & stop bit + movwf bitcount ;Load the counter + bsf STATUS,RP0 + bsf PIE1,TMR2IE ;Enable timer 2 interrupts + bcf STATUS,RP0 + movlw PERIOD + movwf TMR2 ;Prepare timer 2 to overflow asap + bsf T2CON,TMR2ON ;Start timer 2 +PrtDebugPointer pcall PrintDebug ;Report the debug value, if necessary + return + +ReportPower bcf PowerReport ;Print the report only once + movlw NormalPowerStr ;Normal Power + btfss RaisedPower + goto ReportPowerJ2 + movlw MediumPowerStr ;Medium Power + btfss HighPower + goto ReportPowerJ1 + movfw LineVoltage ;Check the line voltage + sublw V_OPEN ;Thermostat disconnected? + skpc + return ;Don't report the power change + movlw HighPowerStr ;High Power +ReportPowerJ1 lcall PrintString + movlw PowerStr +ReportPowerJ2 pcall PrintStringNL ;Print the new power setting + return + +; IdleTimer is called whenever timer 0 overflows +IdleTimer bcf INTCON,TMR0IF ;Clear Timer 0 overflow flag + decfsz SecCounter,F ;Decrease second counter + goto IdleTimerJ1 + btfsc BoilerAlive ;Ever received a message from boiler? + incf repeatcount,F ;Keep track of repeated messages + bsf OneSecond ;Trigger new message in stand-alone mode + movlw ONESEC + movwf SecCounter ;Reload second counter + incfsz boilercom,W ;Increment variable without rolling over + movwf boilercom + movlw 5 + subwf boilercom,W ;Check for 5 seconds without a message + skpnc + bcf SlaveOut ;Prevent unwanted heating +IdleTimerJ1 btfss NoBreak + incf BreakTimer,F + btfss T2CON,TMR2ON + bsf ADCON0,GO ;Start A/D conversion + pagesel Serial + btfsc ChangeMode ;Is a request to change mode pending? + call SetMonitorMode + pagesel Print + comf errornum,W ;Suppressing errors? + skpnz + clrf errornum ;Re-enable errordetection + tstf errornum ;Check if any errors were detected + skpz + call PrintError ;Report the error on the serial intf + pagesel Main + tstf errortimer + skpz + decfsz errortimer,F + return + movlw 'E' + pcall SwitchOffLED ;Switch off the error LED + return + +FlashTimer bcf PIR1,TMR1IF ;Clear timer overrun flag + btfsc ChangeTime + decf minutetimer,F + btfsc OverrideReq + btfsc OverrideAct + return ;No override request pending + ;Override requested, but not active. Flash the override LED + movlw 1 << 7 ;Mask for the OverrideBlink bit + xorwf override,F + andwf override,W + movlw 'O' + pcall SwitchLED + return + +SummaryReport btfsc txavailable,6 ;Buffer filled less than 80%? + return ;Not enough space in the transmit buffer + pcall PrintStoredVal + movlw SUMMARYFIELDS + subwf valueindex,W + skpnc ;Check if the summary is complete + goto SummaryDone + movlw ',' ;Separator + pcall PrintChar + return +SummaryDone bcf SummaryRequest + pcall NewLine + return + +ProcessOutput btfss PIR1,TXIF ;Check if the transmit buffer is empty + return + movfw txpointer ;Get the offset of the next character + addlw txbuffer ;Add the start of the buffer + movwf FSR ;Setup indirect access + bsf STATUS,IRP ;The buffer is in bank 2 + movfw INDF ;Get the character from the buffer + bcf STATUS,IRP ;Restore the status register to normal + movwf TXREG ;Load the serial transmitter + incf txpointer,F ;Move to the next character + movlw TXBUFSIZE + xorwf txpointer,W ;Check for the end of the buffer + skpnz + clrf txpointer ;Wrap around to the start + decf txavailable,F ;Adjust character count + return + +;This code should not set the carry bit, it may clear the carry +EepromWait bsf STATUS,RP1 ;EEPROM registers are in bank 2&3 + bsf STATUS,RP0 + btfss EECON1,WR ;Check if a write is in progress + return ;Safe to modify the EEPROM registers + bcf STATUS,RP0 ;Switch back to bank 0 + bcf STATUS,RP1 + btfss PIR1,RCIF ;Check for serial receiver activity + goto EepromWait ;Wait some more + movwf temp2 ;Preserve value in W register + call SerialReceive ;Prevent receiver overrun + movfw temp2 ;Restore original W register value + goto EepromWait ;Wait some more + +ReadEpromData call EepromWait ;Wait for any pending EEPROM activity + clrf EECON1 ;Read from DATA EEPROM + bcf STATUS,RP0 + movwf EEADR ;Setup the EEPROM data address + bsf STATUS,RP0 + bsf EECON1,RD ;Start the EEPROM read action + bcf STATUS,RP0 + movfw EEDATA ;Read the data from EEPROM + bcf STATUS,RP1 ;Switch back to bank 0 + return + +;The Opentherm specification mentions the following special circumstance: +;"The boiler unit must support an important installation feature which allows +;the terminals at the boiler to be short-circuited to simulate a heat demand +;such as can be done with existing on/off boilers. The boiler unit should +;interpret the short-circuit as a heat demand within15 secs of the short- +;circuit being applied. This must be supported by both OT/+ and OT/- boilers. +; +;It is allowable that this can implemented by a software-detection method. The +;software short-circuit condition is defined as a low-voltage state (Vlow) with +;no valid communications frame for at least 5 seconds." +; +;Without special precautions, this situation would occur when the thermostat is +;disconnected while the gateway is still powered up. Without a thermostat, the +;gateway does not generate any messages towards the boiler and the default idle +;level of the Opentherm signal is the mentioned low-voltage state. +; +;To prevent an unintended heat demand when the thermostat is disconnected, the +;thermostat interface is monitored periodically between messages. If the master +;interface is found to be at a high-voltage state, the slave interface is also +;put to the high-voltage state. + +;Measure the voltage on the thermostat line. A value between 10 and 45 is the +;normal idle line value. Between 95 and 115 is the opentherm high level. We'll +;check for a value below 6 (short circuit) or above 122 (open) to detect an +;on/off thermostat (or no thermostat at all). + +CheckThermostat bcf PIR1,ADIF ;Clear flag for next round + movfw ADRESH ;Get the A/D result + xorwf LineVoltage,W ;Swap values of W and LineVoltage + xorwf LineVoltage,F + xorwf LineVoltage,W ;-- + subwf LineVoltage,W ;Calculate the difference + skpc + sublw 0 ;Turn into absolute value + andlw b'11111100' ;Check for limited deviation + skpz + goto Unstable + movfw LineVoltage ;Get the A/D result again + sublw V_OPEN ;Line open? + skpnc + btfsc DisconnectEvent ;Interrupt routine detected disconnect + goto OpenCircuit ;No opentherm thermostat connected + sublw V_OPEN - V_SHORT ;Line shorted? + skpc + goto ShortCircuit ;Heat demand from on/off thermostat + btfss NoThermostat + return +;Any opentherm thermostat will always start in low power mode, so we want to +;see a low line voltage before considering the thermostat to be reconnected. + sublw V_LOW - V_SHORT ;No short, but logical low level? + skpc + goto ThermostatEnd ;Not a logical low opentherm level +Connect bcf NoThermostat ;Thermostat was reconnected + movlw TConnect + pcall PrintStringNL + return +ShortCircuit bsf HeatDemand + goto DumbThermostat +OpenCircuit bcf HeatDemand +DumbThermostat btfsc NoThermostat + goto ThermostatEnd +Disconnect bsf NoThermostat ;Thermostat has been disconnected + movlw TDisconnect ;Report the thermostat was disconnected + pcall PrintStringNL + bcf T2CON,TMR2ON ;Stop timer 2 + bcf INTCON,PEIE ;Prevent peripheral interrupts + bsf STATUS,RP0 + bcf CMCON,C1INV ;Make sure the line is in low power mode + swapf CMCON,W ;Read the new comparator states + bcf STATUS,RP0 + bcf PIR2,CMIF ;Clear the interrupt caused by inverting + bsf INTCON,PEIE ;Re-enable peripheral interrupts + xorwf msgflags,W ;Get the saved comparator state + andlw b'00001100' ;Filter out the comparator bits + xorwf msgflags,F ;Update the saved state + clrf tstatflags ;Forget all thermostat related info + btfss TStatManual ;Manually configure thermostat model + clrf remehaflags + clrf override + clrf setpoint1 + clrf setpoint2 + movlw 'P' + pcall SwitchOffLED ;Switch off Raised Power LED + return +Unstable btfss NoThermostat + return +ThermostatEnd btfss MonitorMode ;Can't send messages in monitor mode + btfss OneSecond ;Time to send the next message? + return + movlw T_READ + movwf byte1 + clrf byte3 + clrf byte4 + call SelectMessage ;Get a message to send from the table + movwf byte2 + movwf originalreq + pcall UnknownMask + skpz ;Check if the boiler supports the msg + goto NoAltMessage + call UserMsg ;Get a msg from the list of alternatives + movwf byte2 + movwf originalreq +NoAltMessage pcall SetParity ;Calculate the parity + bcf AlternativeUsed ;Prevent unnecessary processing + call SendMessage ;Start sending the message + clrf tstatflags ;Message queued - clear all flags + clrf boilercom ;Sent a message to the boiler + return +SelectMessage movlw high MessageTable + movwf PCLATH + movfw MessagePtr + addlw low MessageTable + skpnc + incf PCLATH,F + movwf PCL +MessageTable goto StatusMsg + retlw MSG_BOILERTEMP + goto CtrlSetptMsg + goto UserMsg + retlw MSG_MODULATION + retlw MSG_OUTSIDETEMP + retlw MSG_RETURNTEMP + goto MaxModulation + goto UserMsg + goto StatusMsg + retlw MSG_BOILERTEMP + goto CtrlSetptMsg + retlw MSG_DHWSETPOINT + retlw MSG_MAXCHSETPOINT + goto UserMsg + retlw MSG_CHPRESSURE + +UserMsg pcall Alternative ;Find a user defined message ID + btfss NoAlternative ;Check if any message was found + return + clrf byte1 ;Clear out the fall-back message + clrf byte3 + clrf byte4 + ;Fall through so a status message is used as filler +StatusMsg tstf controlsetpt1 ;User specified setpoint? + skpz + btfsc CHModeOff + goto StatusMsgJmp1 + bsf byte3,0 ;Controlsetpt set and CHModeOff == 0 +StatusMsgJmp1 skpnz + btfss HeatDemand + goto StatusMsgJmp2 + bsf byte3,0 ;Controlsetpt clear and HeatDemand == 1 +StatusMsgJmp2 btfsc HotWaterEnable ;Check hot water user setting + bsf byte3,1 ;Enable domestic hot water + retlw MSG_STATUS ;Send a status message + +CtrlSetptMsg bsf byte1,4 ;This is a write-data request + movfw controlsetpt2 + movwf byte4 + movfw controlsetpt1 ;Check the user defined ctrl setpoint + skpz ;No user defined control setpoint + goto CtrlSetptMsgRet + movlw 10 ;Default control setpoint + btfss HeatDemand ;Check if lines are short-circuited + goto CtrlSetptSimple ;Send control setpoint for heating off + movfw maxchsetpoint2 + movwf byte4 + movfw maxchsetpoint1 ;Did user specify a max ch setpoint? + skpz + goto CtrlSetptMsgRet + movfw chsetpointmax ;Use the maximum control setpoint +CtrlSetptSimple clrf byte4 +CtrlSetptMsgRet movwf byte3 ;Specify the desired control setpoint + retlw MSG_CTRLSETPT ;Send a control setpoint message + +MaxModulation bsf byte1,4 ;This is a write-data request + movfw MaxModLevel ;Get user specified level (default 100%) + movwf byte3 + retlw MSG_MAXMODULATION + +;************************************************************************ + + package Print +PrintString pcall EepromWait ;Wait for any pending EEPROM activity + bcf STATUS,RP0 + movwf EEADR + goto PrintJump +PrintLoop call PrintChar + bsf STATUS,RP1 + incf EEADR,F +PrintJump bsf STATUS,RP0 + bsf EECON1,RD + bcf STATUS,RP0 + movfw EEDATA + bcf STATUS,RP1 + skpz + goto PrintLoop + return + +PrintStringNL call PrintString +NewLine movlw 0x0d + call PrintChar + movlw 0x0a + goto PrintChar + +PrintHex movwf temp2 ;Temporarily store the byte + swapf temp2,W ;Get the high nibble + call PrintXChar ;And print it + movf temp2,W ;Next, get the low nibble + ;Fall through to the subroutine to print a hex character +PrintXChar andlw B'00001111' ;Extract the low nible + addlw -10 ;Check if is a digit or alpha char + skpnc + addlw 'A' - '9' - 1 ;Bridge the gap in the ASCII table + addlw 10 ;Re-add the amount subtracted earlier +PrintDigit bsf LeadingZero ;Don't skip any future zeroes + addlw '0' ;Convert to ASCII digit +PrintChar tstf txavailable ;Check if some data is already waiting + skpnz + btfss PIR1,TXIF ;Check the transmit register + goto PrintBuffer ;Need to put the byte into the buffer + movwf TXREG ;Can transmit immediately + retlw 0 +PrintBuffer movwf FSR ;Abuse FSR for temporary storage + movfw txpointer ;Current position in the transmit buffer + addwf txavailable,W ;Add number of pending characters + addlw -TXBUFSIZE ;Check for wrap-around + skpc + addlw TXBUFSIZE ;No wrap, recalculate original value + addlw txbuffer ;Next free position in transmit buffer + xorwf FSR,W ;Exchange FSR and W + xorwf FSR,F + xorwf FSR,W ;-- + bsf STATUS,IRP ;Buffer is in bank 2 + movwf INDF ;Store byte in the buffer + bcf STATUS,IRP ;Restore status register to normal + movfw txavailable ;Get number of pending characters + xorlw TXBUFSIZE ;Compare against buffer size + skpz ;Buffer full? + incf txavailable,F ;One more character in the buffer + retlw 0 + +PrintRcvdMsg movlw 'T' + btfsc MsgResponse + movlw 'B' +PrintMessage btfsc HideReports + return ;Don't print the message + call PrintChar + movfw byte1 + call PrintHex + movfw byte2 + call PrintHex + movfw byte3 + call PrintHex + movfw byte4 + call PrintHex + goto NewLine + +PrintError btfsc PowerReport ;Pending power change report? + return ;Error probably due to the power change + movlw ErrorStr + call PrintString + movfw errornum + call PrintHex + call NewLine + movlw 'E' + btfss errornum,0 + call PrintMessage ;Print message for error 2 and 4. + clrf errornum +PrintDebug movfw debug + skpnz + return + movwf FSR + movfw INDF + movwf temp + movfw debug + call PrintHex + movlw '=' + call PrintChar + movfw temp + call PrintHex + goto NewLine + +PrintStoredVal movfw valueindex + addwf valueindex,W + addlw valuestorage + movwf FSR + bsf STATUS,IRP + movfw INDF + movwf float1 + incf FSR,F + movfw INDF + movwf float2 + bcf STATUS,IRP + movlw high StoreProcTable + movwf PCLATH + movfw valueindex + incf valueindex,F + addlw low StoreProcTable + skpnc + incf PCLATH,F + movwf PCL +StoreProcTable goto PrintStatus ;Message ID 0 + goto PrintFloat ;Message ID 1 + goto PrintStatus ;Message ID 6 + goto PrintFloat ;Message ID 14 + goto PrintBytes ;Message ID 15 + goto PrintFloat ;Message ID 16 + goto PrintFloat ;Message ID 17 + goto PrintFloat ;Message ID 18 + goto PrintFloat ;Message ID 24 + goto PrintFloat ;Message ID 25 + goto PrintFloat ;Message ID 26 + goto PrintSigned ;Message ID 27 + goto PrintFloat ;Message ID 28 + goto PrintBytes ;Message ID 48 + goto PrintBytes ;Message ID 49 + goto PrintFloat ;Message ID 56 + goto PrintFloat ;Message ID 57 + goto PrintShort ;Message ID 116 + goto PrintShort ;Message ID 117 + goto PrintShort ;Message ID 118 + goto PrintShort ;Message ID 119 + goto PrintShort ;Message ID 120 + goto PrintShort ;Message ID 121 + goto PrintShort ;Message ID 122 + goto PrintShort ;Message ID 123 + +PrintStatus movfw float1 + call PrintBinary + movlw '/' + call PrintChar + movfw float2 +PrintBinary movwf temp + movlw 8 + movwf loopcounter +PrintBinaryLoop rlf temp,F + call PrintBoolean + decfsz loopcounter,F + goto PrintBinaryLoop + return + + errorlevel -219 ;Ignore warning about invalid RAM addr +PrintBoolean rlf 7,W ;Trick: Address 7 reads as all 0's + errorlevel +219 ;Turn warning back on + goto PrintDigit + +PrintSigned btfss float1,7 ;Check if value is negative + goto PrintFloat + comf float1,F ;Negate the value + comf float2,F + incf float2,F + skpnz + incf float1,F ;-- + movlw '-' + call PrintChar ;Print a minus sign + ;Fall through to print the absolute value +;If the low byte of the floating point value is 0xff, it would translate to a +;fraction of .996, which should be rounded up to the next full number. This +;needs to be checked before the integer part of the value can be printed. +PrintFloat incfsz float2,W ;Check if the 2nd byte is 0xff + goto PrintFloatJ1 + incf float1,F ;Round up to the next integral number + clrf float2 ;-- +PrintFloatJ1 movfw float1 + call PrintByte + movlw 100 + movwf temp + movfw float2 + call Multiply + btfsc float2,7 + incf float1,F ;Rounding + movfw float1 + movwf temp + movlw '.' + goto PrintFraction + +PrintBytes movfw float1 + call PrintByte + movlw '/' + call PrintChar + movfw float2 +PrintByte bcf LeadingZero + movwf temp + addlw -100 + skpc + goto PrintByteJ1 + movwf temp + addlw -100 + skpc + goto Print100 + movwf temp + movlw '2' + goto PrintFraction +Print100 movlw '1' +PrintFraction call PrintChar +PrintDecimal bsf LeadingZero +PrintByteJ1 clrf loopcounter + movfw temp +PrintByteL1 movwf temp ;BCD + addlw -10 + skpc + goto PrintByteJ2 + incf loopcounter,F + goto PrintByteL1 +PrintByteJ2 movfw loopcounter + skpnz + btfsc LeadingZero + call PrintLDigit + movfw temp +PrintLDigit bsf LeadingZero + goto PrintDigit + +PrintShort movlw 16 + movwf loopcounter + clrf tempvar0 + clrf tempvar1 + clrf tempvar2 + goto PrintShortJump +PrintShortLoop movfw tempvar2 + call AdjustBCD + movwf tempvar2 + movfw tempvar1 + call AdjustBCD + movwf tempvar1 +PrintShortJump rlf float2,F + rlf float1,F + rlf tempvar2,F + rlf tempvar1,F + rlf tempvar0,F + decfsz loopcounter,F + goto PrintShortLoop + bcf LeadingZero + movfw tempvar0 + call PrintBCD + movfw tempvar1 + call PrintBCD + movfw tempvar2 + call PrintBCD + btfsc LeadingZero + return + goto PrintDigit ;W is always 0 here +AdjustBCD addlw 0x33 + movwf temp + btfss temp,3 + addlw -0x03 + btfss temp,7 + addlw -0x30 + return +PrintBCD movwf temp + swapf temp,W + call PrintBCDDigit + movfw temp +PrintBCDDigit andlw b'1111' + skpnz + btfsc LeadingZero + goto PrintDigit + return + +PrintSetting movfw rxpointer ;Check that the command is 4 chars long + sublw 4 + skpz + retlw SyntaxError + movlw high PrintTable + movwf PCLATH + movfw rxbuffer + 3 + sublw 'Z' + sublw 'Z' - 'A' + skpc + retlw BadValue + addlw low PrintTable + skpnc + incf PCLATH,F + movwf PCL +PrintTable retlw PrintSettingA ;PR=A + retlw PrintSettingB ;PR=B + retlw PrintSettingC ;PR=C + retlw BadValue ;PR=D + retlw BadValue ;PR=E + retlw BadValue ;PR=F + goto PrintSettingG ;PR=G + retlw BadValue ;PR=H + goto PrintSettingI ;PR=I + retlw BadValue ;PR=J + retlw BadValue ;PR=K + retlw PrintSettingL ;PR=L + goto PrintSettingM ;PR=M + retlw BadValue ;PR=N + goto PrintSettingO ;PR=O + goto PrintSettingP ;PR=P + goto PrintSettingQ ;PR=Q + goto PrintSettingR ;PR=R + goto PrintSettingS ;PR=S + goto PrintSettingT ;PR=T + retlw BadValue ;PR=U + goto PrintSettingV ;PR=V + goto PrintSettingW ;PR=W + retlw BadValue ;PR=X + retlw BadValue ;PR=Y + retlw BadValue ;PR=Z + +PrintSettingID movfw rxbuffer + 3 + call PrintChar + movlw '=' + goto PrintChar + +PrintSettingV call PrintSettingID +PrintRefVoltage movfw settings + addlw -3 + goto PrintXChar + +PrintSettingO call PrintSettingID +PrintOverride tstf override ;Any override flags set? + movlw 'N' + skpnz + goto PrintChar ;No override + movfw setpoint1 ;Override setpoint set? + xorwf setpoint2,W + movlw 'n' + skpnz + goto PrintChar ;Waiting to clear the override +PrintOvrSetPt swapf RemOverrideFunc,W ;Get the current bits in lower nibble + xorwf RemOverrideFunc,W ;Determine the changed bits + andlw b'1111' ;Only consider the lower nibble + btfss OverrideFunc ;Lower nibble contains pending request + xorwf RemOverrideFunc,F ;Harmless to overwrite lower nibble + movlw 'T' + btfss ProgChangePrio ;Either pending or current state + movlw 'C' + btfss OverrideAct ;Override accepted by the thermostat? + iorlw 0x20 ;Switch to lowercase + call PrintChar ;Print the override type + movfw setpoint1 ;Copy the setpoint + movwf float1 + movfw setpoint2 + movwf float2 ;-- + goto PrintFloat ;Print as a floating point value + +PrintSettingM call PrintSettingID +PrintGateway movlw 'G' + btfsc MonitorMode + movlw 'M' + goto PrintChar + +PrintSettingQ call PrintSettingID + swapf mode,W + andlw b'111' + addlw ResetReasonStr + pcall ReadEpromData + goto PrintChar + +PrintSettingW call PrintSettingID +PrintHotWater movlw '0' + btfsc HotWaterEnable + movlw '1' + btfss HotWaterSwitch + movlw 'A' + goto PrintChar + +PrintSettingP call PrintSettingID +PrintPowerlevel btfss RaisedPower + retlw NormalPowerStr + movlw MediumPowerStr + btfsc HighPower + movlw HighPowerStr + call PrintString + retlw PowerStr + +PrintSettingR call PrintSettingID +PrintRemeha movlw 'D' + btfsc TStatRemeha + movlw 'R' + btfsc TStatCelcia + movlw 'C' + btfsc TStatISense + movlw 'I' + goto PrintChar + +PrintSettingT call PrintSettingID +PrintTweaks swapf settings,W + movwf temp + rrf temp,F + rrf temp,F + call PrintBoolean + rrf temp,F + goto PrintBoolean + +PrintSettingI call PrintSettingID + rlf PORTA,W + addlw b'10000000' + call PrintBoolean + rlf PORTA,W + goto PrintBoolean + +PrintSettingG call PrintSettingID +PrintGPIO swapf GPIOFunction,W + goto PrintHex + +PrintSettingS call PrintSettingID +PrintSetback movlw AwaySetpoint1 + pcall ReadEpromData + movwf float1 + movlw AwaySetpoint2 + pcall ReadEpromData + movwf float2 + goto PrintFloat + +PrintSummary movfw rxpointer + sublw 4 + skpz + retlw SyntaxError + movfw rxbuffer + 3 + sublw '1' + sublw '1' - '0' + skpc + retlw BadValue + skpnz + goto PrintSummaryOff + clrf valueindex + bsf SummaryRequest + bsf HideReports + goto PrintSummaryOn +PrintSummaryOff bcf SummaryRequest + bcf HideReports +PrintSummaryOn lgoto PrintDigit + +;Multiply W by temp - result in float1:float2 +Multiply clrf float1 + clrf float2 + xorlw 8 + movwf loopcounter + xorlw 8 + xorwf loopcounter,F +MultiplyLoop clrc + rlf float2,F + rlf float1,F + rlf temp,F + skpnc + addwf float2,F + skpnc + incf float1,F + decfsz loopcounter,F + goto MultiplyLoop + return + +;************************************************************************ +;Process an incoming OpenTherm message +;************************************************************************ + package Message +HandleMessage btfsc MsgResponse ;Treat requests and responses differently + goto HandleResponse + +HandleRequest btfsc byte1,4 ;For Write-Data messages, ... + call StoreValue ;... store the value from the thermostat + movfw byte1 + movwf originaltype ;Save the original message type + movfw byte2 + xorwf originalreq,W ;Compare against previous message + skpz + clrf repeatcount ;Message is different -> reset counter + xorwf originalreq,F ;Save the original message ID + movfw byte3 + movwf databyte1 ;Save original data byte #1 + movfw byte4 + movwf databyte2 ;Save original data byte #2 + bcf AlternativeUsed + bsf OverrideUsed ;Assume only data values will be changed + call TreatMessage ;Process the request from the thermostat + clrf boilercom ;Going to send a message to the boiler + call UnknownMask ;Check if ID is unsupported by boiler + skpnz ;Data ID is not known to be unsupported + goto SendAltRequest + btfss AlternativeUsed ;Was the request modified in some way? + return ;Send message unmodified to the boiler + goto SetParity ;Calculate the parity of the new message +SendAltRequest bsf AlternativeUsed ;Probably going to send a different msg + bcf OverrideUsed ;Changes will be more drastic + call Alternative ;Get the alternative message to send + btfss AlternativeUsed + return ;There were no other candidates + movwf byte2 ;Fill in the alternative Message ID + goto SetParity ;Calculate the parity of the new message + +;Vendor specific ID's can go through the code below without causing any trouble +;because the mask returned by UnknownMask will be 0. +HandleResponse bsf BoilerAlive ;Received a message from the boiler + movfw byte2 + xorwf prioritymsgid,W ;Response matches ID of priority msg? + skpnz + bcf PriorityMsg ;Priority message answered + btfss byte1,4 ;For Read-Ack messages, ... + call StoreValue ;... store the value from the boiler + call UnknownMask ;Check boiler support for the DataID + btfsc byte1,5 + btfss byte1,4 + goto MsgSupported ;No Unknown-DataID response + skpnz + goto MsgUnsupported ;Already found 3 consecutive failures + andlw b'01010101' ;Get lower bit of the mask + addwf INDF,F ;Increase failure count + goto HandleRespJump +MsgUnsupported movfw byte2 ;Get the message ID + pcall DeleteAlt ;Remove from the list of alternatives + goto HandleRespJump +MsgSupported xorlw -1 ;Invert the mask + btfss NoResetUnknown ;Do nothing for administrative requests + andwf INDF,F ;Reset the failure count +HandleRespJump bcf NoResetUnknown + bsf BoilerResponse + btfsc Unsolicited + goto RemoteCommand + btfsc AlternativeUsed + goto SendAltResponse + call TreatMessage ;Process the received response + btfsc AlternativeUsed ;Was the response modified in some way? + btfsc NoThermostat ;And a thermostat is connected? + return + goto SetParity +SendAltResponse call TreatMessage ;Process the received response + movlw b'01000000' ;Response bit + btfss OverrideUsed + movlw b'01110000' ;Boiler does not support the message + call CreateMessage ;Turn the request into a response + bcf BoilerResponse + btfss OverrideUsed ;Does the replacement need processing? + call TreatMessage ;Process the message to return +SetParity movfw byte4 ;Get the last byte + xorwf byte3,W ;Calculate the sum of the last 2 bytes + xorwf byte2,W ;Calculate the sum of the last 3 bytes + bcf MsgParity ;Make sure the parity bit is cleared + xorwf byte1,W ;Calculate the sum of all four bytes + movwf temp ;Save in temporary storage space + swapf temp,F ;Swap the nibbles of the checksum + xorwf temp,W ;Calculate the sum of the two nibbles + movwf temp ;Save the result again + rrf temp,F ;Shift the checksum 2 bits + rrf temp,F + xorwf temp,F ;Combine two sets of two bits + movlw 1 ;Setup a mask to be used later + btfsc temp,1 ;Check if bit 1 is set + xorwf temp,F ;If so, invert bit 0 + btfsc temp,0 ;Check the calculated parity + bsf MsgParity ;Fill in the parity bit for even parity + movlw 'R' + btfsc MsgResponse + movlw 'A' + lgoto PrintMessage + +RemoteCommand call TreatMessage ;Process the received message + movlw B_WACK + movwf byte1 + movlw MSG_REMOTECMD + movwf byte2 + movlw RCMD_TELEFOON + movwf byte3 + movfw celciasetpoint + movwf byte4 + bcf Unsolicited + goto SetParity + +;Define special treatment based on the message ID. This code will be called in +;three situations: +;1. A request was received from the thermostat. +;2. A response was received from the boiler. +;3. An alternative message has been sent to the boiler and a fake response has +; been created to be sent back to the thermostat. +;Which of the three situations applies can be determined by checking the +;MsgResponse and BoilerResponse flags. The flags will have the following states: +;1. MsgResponse = 0, BoilerResponse = X +;2. MsgResponse = 1, BoilerResponse = 1 +;3. MsgResponse = 1, BoilerResponse = 0 +;If the code modifies the message, it must set the AlternativeUsed flag. This +;will cause the parity to be recalculated and the modified message is reported +;on the serial interface. + +TreatMessage btfsc byte2,7 + goto FakeResponse ;Limited support for Vender specific ID + movlw high messagetable + movwf PCLATH + movfw byte2 + addlw low messagetable + skpnc + incf PCLATH,F + movwf PCL +messagetable goto MessageID0 ;Data ID 0 + goto MessageID1 ;Data ID 1 + goto MessageID2 ;Data ID 2 + goto MandatoryWord ;Data ID 3 + goto MessageID4 ;Data ID 4 + goto MessageID5 ;Data ID 5 + goto MessageID6 ;Data ID 6 + goto FakeResponse ;Data ID 7 + goto FakeResponse ;Data ID 8 + goto MessageID9 ;Data ID 9 + goto TSPBufferSize ;Data ID 10 + goto TSPReadEntry ;Data ID 11 + goto TSPBufferSize ;Data ID 12 + goto TSPReadEntry ;Data ID 13 + goto MessageID14 ;Data ID 14 + goto WordResponse ;Data ID 15 + goto MessageID16 ;Data ID 16 + goto MandatoryWord ;Data ID 17 + goto WordResponse ;Data ID 18 + goto WordResponse ;Data ID 19 + goto MessageID20 ;Data ID 20 + goto WordResponse ;Data ID 21 + goto WordResponse ;Data ID 22 + goto FakeResponse ;Data ID 23 + goto WordResponse ;Data ID 24 + goto MandatoryWord ;Data ID 25 + goto WordResponse ;Data ID 26 + goto MessageID27 ;Data ID 27 + goto WordResponse ;Data ID 28 + goto WordResponse ;Data ID 29 + goto WordResponse ;Data ID 30 + goto WordResponse ;Data ID 31 + goto WordResponse ;Data ID 32 + goto WordResponse ;Data ID 33 + goto WordResponse ;Data ID 34 + goto WordResponse ;Data ID 35 + goto FakeResponse ;Data ID 36 + goto FakeResponse ;Data ID 37 + goto FakeResponse ;Data ID 38 + goto FakeResponse ;Data ID 39 + goto FakeResponse ;Data ID 40 + goto FakeResponse ;Data ID 41 + goto FakeResponse ;Data ID 42 + goto FakeResponse ;Data ID 43 + goto FakeResponse ;Data ID 44 + goto FakeResponse ;Data ID 45 + goto FakeResponse ;Data ID 46 + goto FakeResponse ;Data ID 47 + goto MessageID48 ;Data ID 48 + goto MessageID49 ;Data ID 49 + goto WordResponse ;Data ID 50 + goto WordResponse ;Data ID 51 + goto WordResponse ;Data ID 52 + goto WordResponse ;Data ID 53 + goto WordResponse ;Data ID 54 + goto WordResponse ;Data ID 55 + goto MessageID56 ;Data ID 56 + goto MessageID57 ;Data ID 57 + goto WordResponse ;Data ID 58 + goto WordResponse ;Data ID 59 + goto WordResponse ;Data ID 60 + goto WordResponse ;Data ID 61 + goto WordResponse ;Data ID 62 + goto WordResponse ;Data ID 63 + goto FakeResponse ;Data ID 64 + goto FakeResponse ;Data ID 65 + goto FakeResponse ;Data ID 66 + goto FakeResponse ;Data ID 67 + goto FakeResponse ;Data ID 68 + goto FakeResponse ;Data ID 69 + goto ByteResponse ;Data ID 70 + goto MessageID71 ;Data ID 71 + goto WordResponse ;Data ID 72 + goto WordResponse ;Data ID 73 + goto WordResponse ;Data ID 74 + goto WordResponse ;Data ID 75 + goto WordResponse ;Data ID 76 + goto WordResponse ;Data ID 77 + goto WordResponse ;Data ID 78 + goto WordResponse ;Data ID 79 + goto WordResponse ;Data ID 80 + goto WordResponse ;Data ID 81 + goto WordResponse ;Data ID 82 + goto WordResponse ;Data ID 83 + goto WordResponse ;Data ID 84 + goto WordResponse ;Data ID 85 + goto WordResponse ;Data ID 86 + goto WordResponse ;Data ID 87 + goto TSPBufferSize ;Data ID 88 + goto TSPReadEntry ;Data ID 89 + goto TSPBufferSize ;Data ID 90 + goto TSPReadEntry ;Data ID 91 + goto FakeResponse ;Data ID 92 + goto FakeResponse ;Data ID 93 + goto FakeResponse ;Data ID 94 + goto FakeResponse ;Data ID 95 + goto FakeResponse ;Data ID 96 + goto FakeResponse ;Data ID 97 + goto FakeResponse ;Data ID 98 + goto FakeResponse ;Data ID 99 + goto MessageID100 ;Data ID 100 + goto ByteResponse ;Data ID 101 + goto WordResponse ;Data ID 102 + goto WordResponse ;Data ID 103 + goto WordResponse ;Data ID 104 + goto TSPBufferSize ;Data ID 105 + goto TSPReadEntry ;Data ID 106 + goto TSPBufferSize ;Data ID 107 + goto TSPReadEntry ;Data ID 108 + goto FakeResponse ;Data ID 109 + goto FakeResponse ;Data ID 110 + goto FakeResponse ;Data ID 111 + goto FakeResponse ;Data ID 112 + goto WordResponse ;Data ID 113 + goto WordResponse ;Data ID 114 + goto WordResponse ;Data ID 115 + goto WordResponse ;Data ID 116 + goto WordResponse ;Data ID 117 + goto WordResponse ;Data ID 118 + goto WordResponse ;Data ID 119 + goto WordResponse ;Data ID 120 + goto WordResponse ;Data ID 121 + goto WordResponse ;Data ID 122 + goto WordResponse ;Data ID 123 + goto FakeResponse ;Data ID 124 + goto MessageID125 ;Data ID 125 + goto MessageID126 ;Data ID 126 + goto WordResponse ;Data ID 127 + +messageinv movlw B_INV + goto setbyte1 +messageack movfw originaltype ;Get original request type + iorlw b'11000000' ;Turn it into a matching acknowledgement + btfss byte1,7 + andlw b'01111111' ;Match parity bit of the received msg +setbyte1 xorwf byte1,W ;Check if the byte is different + skpz + bsf AlternativeUsed ;Going to modify the message + xorwf byte1,F ;Set the byte + retlw 0 + +setbyte3 xorwf byte3,W ;Check if the byte is different + skpz + bsf AlternativeUsed ;Going to modify the message + xorwf byte3,F ;Set the byte + retlw 0 + +datarestore movfw databyte1 + call setbyte3 + movfw databyte2 +setbyte4 xorwf byte4,W ;Check if the byte is different + skpz + bsf AlternativeUsed ;Going to modify the message + xorwf byte4,F ;Set the byte + retlw 0 + +;DataID 0: Status +;The gateway may want to manipulate the status message generated by the +;thermostat for two reasons: The domestic hot water enable option specified +;via a serial command may differ from what the thermostat sent. And if a +;control setpoint has been specified via a serial command, central heating +;mode must be enabled for the setpoint to have any effect. +;When a status response comes back from the boiler, several LED states are +;updated. Also if the master status byte was changed before sending it to the +;boiler, the original master status must be restored before the message is +;returned to the thermostat, so it doesn't get confused +MessageID0 btfsc MsgResponse ;Status request or response? + goto statusreturn +readstatus call MandatoryID ;Remove any blacklisting for the DataID + movfw byte3 + tstf controlsetpt1 + skpnz + goto normalstatus + iorlw b'1' ;Enable central heating mode + btfsc CHModeOff + andlw ~b'1' ;Disable central heating mode +normalstatus btfss HotWaterSwitch + goto setbyte3 ;Don't manipulate hot water enable + andlw ~b'10' ;Domestic hot water enable off + btfsc HotWaterEnable + iorlw b'10' ;Domestic hot water enable on + goto setbyte3 + +statusreturn btfsc byte1,5 + return ;Not a valid response from the boiler + movfw byte4 + movwf databyte2 ;Copy slave status to response message + andlw b'01000001' ;Check fault bit and diagnostic bit + skpnz + bcf BoilerFault ;Clear any previous boiler faults + skpz + btfsc BoilerFault ;New boiler fault? + goto statusmaint + bsf BoilerFault ;Remember a fault was reported + movlw MSG_FAULTCODE ;ASF-flags/OEM-fault-code message + movwf prioritymsgid ;Schedule request for more information + bsf PriorityMsg +statusmaint movlw 'M' + call SwitchLED ;Update the maintenance LED + movfw byte4 + andlw b'00001000' ;Check the flame status bit + movlw 'F' + call SwitchLED ;Update the flame LED + movfw byte4 + andlw b'00000100' ;Check the DHW mode bit + movlw 'W' + call SwitchLED ;Update the hot water LED + movfw byte4 + andlw b'00000010' ;Check the CH mode bit + movlw 'H' + call SwitchLED ;Update the central heating LED + movfw byte3 + andlw b'00000010' ;Check the DHW enable bit + movlw 'C' + goto SwitchLED ;Update the comfort mode LED + +;DataID 1: Control Setpoint +;If a control setpoint has been specified via a serial command, the specified +;value is put into the message before sending it to the boiler. +;If the boiler indicates that the control setpoint is invalid, the value +;specified by the user is cleared. +MessageID1 btfsc MsgResponse ;Request or response? + goto ctrlsetptreturn + call MandatoryID ;Prevent the DataID getting blacklisted + movfw controlsetpt1 ;Check if a setpoint has been specified + xorwf controlsetpt2,W + skpz + btfss byte1,4 ;WriteData request? + return ;Continue with normal processing + movfw controlsetpt1 ;Fill in the specified setpoint + call setbyte3 + movfw controlsetpt2 + goto setbyte4 + +ctrlsetptreturn btfss byte1,5 + return ;Setpoint acknowledged by boiler + btfsc byte1,4 + return + tstf controlsetpt1 + skpnz + return + clrf controlsetpt1 ;Zap the invalid value + clrf controlsetpt2 + return + +;DataID 2: Master Configuration +;The gateway will provide Smart Power support, even if the boiler doesn't. +MessageID2 btfss MsgResponse + goto BrandDetect ;Detect thermostat manufacturer + movlw b'1' ;Don't know what the other bits are + andwf databyte1,W ;... so don't acknowledge those. + call setbyte3 ;Acknowledge Smart Power support + movfw databyte2 ;Make sure the master MemberID in ... + call setbyte4 ;... the response matches the request + goto messageack ;Turn request into acknowledgement +BrandDetect btfsc byte3,0 ;Smart power bit + bsf SmartPower ;Thermostat supports Smart Power + btfsc TStatManual + return ;Don't auto-detect the thermostat model + movfw byte4 ;MemberID code of the master + sublw 11 ;Remeha + skpnz + btfsc TStatRemeha + return + bsf TStatRemeha ;The master is some Remeha thermostat + btfsc SmartPower ;No Smart Power Support? + bsf TStatISense ;Probably an iSense (or qSense) + ;Without Smart Power support, the thermostat can be a Celcia20 + ;or a c-Mix. The Celcia20 periodically sends a Master Product + ;Version message (MsgID 126). So assume the thermostat is not + ;a Celcia20 until it informs us otherwise. + return + +;DataID 4: Remote Request +;The remote request message with a request code of 170 is used by the Celcia +;20 to specify a remote setpoint override. +MessageID4 btfss MsgResponse + return ;Don't modify requests + movlw RCMD_TELEFOON ;Check for the celcia override request + subwf byte3,W + skpz + return ;Todo: Turn UnkDataID into DataInvalid? + call messageack ;Turn request into acknowledgement + movfw celciasetpoint + goto setbyte4 + +;DataID 5: Application-specific flags +;The Remeha iSense RF doesn't always report a room setpoint, but it does ask for +;the Application-specific flags quite regularly. If such a message is received +;after the remote setpoint clear command has been sent, we're going to assume +;the setpoint clear has been accepted. +MessageID5 btfsc MsgResponse ;Only interested in requests + goto WordResponse ;Default handling for responses + btfsc OverrideClr ;No pending clear ... + btfsc OverrideWait ;... or clear command has not been sent? + return ;Do nothing + goto roomsetptclear ;Consider setpoint clear accepted + +;DataID 6: Remote Boiler Parameter flags +;If the boiler doesn't support the DHW Setpoint or Max CH Setpoint parameters, +;the gateway will simulate read access. +MessageID6 btfss MsgResponse + return + bcf InitParameter + clrw ;Assume no support for remote-parameters + btfss byte1,5 + btfsc byte1,4 + goto parameterupdate + movfw byte4 ;Get the remote-parameter r/w flags +parameterupdate xorwf onoffflags,W + andlw b'11' + xorwf onoffflags,F + call messageack ;Turn request into acknowledgement + btfss dhwupdate + bcf InitHotWater ;Boiler doesn't allow DHW setpt update + btfss maxchupdate + bcf InitHeating ;Boiler doesn't allow MaxCH setpt update + movfw byte3 + iorlw b'11' ;The gateway will simulate read access + goto setbyte3 + +;If a remote override setpoint has been specified, return it to the thermostat +MessageID9 btfss MsgResponse + return + call messageack ;Turn request into acknowledgement + btfss OverrideClr + movfw setpoint1 + call setbyte3 ;Data byte 1 has integer part + btfss OverrideClr + movfw setpoint2 + call setbyte4 ;Data byte 2 has the fraction + bcf OverrideWait ;Override has been requested + btfsc TStatCelcia ;Remeha Celcia thermostat? + btfss OverrideReq ;And override requested? + return + btfss OverrideAct ;And override not yet active? + bsf Unsolicited ;Then resend the request on next msg + return + +;Maximum relative modulation level +MessageID14 btfsc MsgResponse ;Status request or response? + return + call MandatoryID ;Prevent the DataID getting blacklisted + btfss UserMaxModLevel + return + movfw MaxModLevel ;Get the user specified max modulation + call setbyte3 ;Set the max modulation level + goto setbyte4 ;Clear the second data byte + +;When the thermostat reports a room setpoint, compare it against the requested +;override setpoint. When it matches, the remote setpoint has been accepted. If +;at some later point it no longer matches, the thermostat has overriden the +;remote setpoint and it must be released. +MessageID16 btfsc MsgResponse + ;We don't want an überclever thermostat to stop sending these + ;messages, so make sure we always return an acknowledgement + goto messageack ;Turn request into acknowledgement + btfsc OverrideReq ;Check for pending override request + btfsc OverrideWait ;Must actually send the request first + return + btfsc OverrideClr + goto roomsetptclear + ;Another "nice" feature of the Remeha iSense thermostat: It + ;doesn't set the setpoint to the exact value requested. So we + ;have to allow for some variation. The delta chosen is 0.125 + ;as that should be enough and is easy to do with bit operations + movfw setpoint2 ;Calcualte the difference between ... + subwf byte4,W ;... the received value and the ... + movwf temp ;... desired setpoint + movfw setpoint1 ;Get the units of the requested setpoint + skpc ;Check for borrow from fractional part + addlw 1 ;Adjust the integer part + subwf byte3,W ;Calculate the difference + skpc + xorlw -1 ;Make the result positive + skpz + goto roomsetptdiff ;Values differ more than 1 degree + skpc ;Carry is set if setpoint <= value + comf temp,F ;Invert the fraction + movlw b'11100000' ;Only the lower 5 bits may be set + andwf temp,W ;Reset the lower 5 bits + skpz ;Result should be 0 for a match + goto roomsetptdiff ;Values differ more than 0.125 degrees + ;Setpoint matches the request + btfsc OverrideAct + return ;Override remains in effect + bsf OverrideAct ;Override was picked up by thermostat + bcf OverrideBlink + movlw 'O' + goto SwitchOnLED ;Switch on the override LED +roomsetptclear bcf OverrideClr ;Override clear data has been sent + movfw setpoint1 + iorwf setpoint2,W ;Check if there is a new setpoint + skpnz + goto roomsetptreset + bsf OverrideWait ;The new setpoint must be sent next + return +roomsetptdiff btfss OverrideAct ;Override currently active? + incf override,F ;Make 3 attempts to set the override + btfss OverrideAct ;Time to abandon override? + return +roomsetptcancel btfsc TStatISense ;Special treatment for iSense thermostat + goto roomsetptreset + clrf setpoint1 ;Reset the setpoint variables + clrf setpoint2 +roomsetptreset clrf override ;Stop requesting a setpoint override + movlw 'O' + goto SwitchOffLED ;Switch off the override LED + +;Return the current date and time, if specified by the user +MessageID20 btfss MsgResponse + return ;No need to process a request + call messageack ;Turn request into acknowledgement + btfss ChangeTime ;Is a new clock value available? + goto datarestore ;Restore the old data bytes and return + movfw clock1 + call setbyte3 + movfw clock2 + goto setbyte4 + +;Return the outside temperature, if specified via the serial interface +MessageID27 btfsc MsgResponse ;Do not modify a request + btfss OutsideTemp ;Do nothing if no outside temp available + return + btfsc OutsideInvalid + goto messageinv + call messageack ;Turn request into acknowledgement + movfw outside1 + call setbyte3 + movfw outside2 + goto setbyte4 + +;Keep track of the boundaries for domestic hot water reported by the boiler +MessageID48 btfss MsgResponse + return + bcf InitHotWater ;Received Hot Water Boundaries response + btfsc byte1,5 ;DataInvalid Or UnknownDataID? + goto WordResponse ;Ignore bad responses + movfw byte3 + movwf dhwsetpointmax ;Remember upper boundary + movfw byte4 + movwf dhwsetpointmin ;Remember lower boundary + goto WordResponse + +;Keep track of the boundaries for central heating reported by the boiler +MessageID49 btfss MsgResponse + return + bcf InitHeating ;Received Heating Boundaries response + btfsc byte1,5 ;DataInvalid Or UnknownDataID? + goto WordResponse ;Ignore bad responses + movfw byte3 + movwf chsetpointmax ;Remember upper boundary + movfw byte4 + movwf chsetpointmin ;Remember lower boundary + goto WordResponse + +;Pass on user defined boundaries for domestic hot water to the boiler +MessageID56 btfsc MsgResponse + goto hotwaterreturn + tstf dhwsetpoint1 ;Check if a setpoint has been specified + btfss byte1,4 ;Change on WriteData + btfsc WaterSetpoint ;Or new setpoint + skpnz ;And a setpoint exists + return ;Continue with normal processing +SetWaterSet movlw T_WRITE + call setbyte1 ;Turn into a write request, if necessary + movfw dhwsetpoint1 + call setbyte3 ;Load the data value high byte + movfw dhwsetpoint2 + call setbyte4 ;Load the data value low byte + retlw MSG_DHWSETPOINT ;Return MsgID when used as alternative +hotwaterreturn bcf WaterSetpoint + btfss byte1,5 + return + btfss byte1,4 + goto InvalidHotWater ;Boiler didn't respond with DataInvalid + ;Boiler does not support the message + call messageack ;Turn request into acknowledgement + movfw dhwsetpoint1 + skpnz + movfw dhwsetpointmax + call setbyte3 + movfw dhwsetpoint2 + goto setbyte4 +InvalidHotWater tstf dhwsetpoint1 + skpnz + return + clrf dhwsetpoint1 ;Zap the invalid value + clrf dhwsetpoint2 + return + +;Pass on user defined boundaries for central heating to the boiler +MessageID57 btfsc MsgResponse + goto maxchsetptret + tstf maxchsetpoint1 ;Check if a setpoint has been specified + btfss byte1,4 ;Change on WriteData + btfsc MaxHeatSetpoint ;Or new setpoint + skpnz ;And a setpoint exists + return ;Continue with normal processing +SetHeatingSet movlw T_WRITE + call setbyte1 ;Turn into a write request, if necessary + movfw maxchsetpoint1 + call setbyte3 ;Load the data value high byte + movfw maxchsetpoint2 + call setbyte4 ;Load the data value low byte + retlw MSG_MAXCHSETPOINT ;Return MsgID when used as alternative +maxchsetptret bcf MaxHeatSetpoint + btfss byte1,5 + return ;Boiler was happy with the message + btfss byte1,4 + goto InvalidHeating + ;Boiler does not support the message + call messageack ;Turn request into acknowledgement + movfw maxchsetpoint1 + skpnz + movfw chsetpointmax + call setbyte3 + movfw maxchsetpoint2 + goto setbyte4 +InvalidHeating tstf maxchsetpoint1 + skpnz + return + clrf maxchsetpoint1 ;Zap the invalid value + clrf maxchsetpoint2 + return + +; Control setpoint ventilation / heat-recovery +MessageID71 clrc ;Prepare for shift operation + rrf ventsetpoint,W ;Get the user defined setpoint + skpnc ;There is no user defined setpoint + btfsc MsgResponse ;Nothing to do for a response + return ;Leave message unmodified + call setbyte4 ;Put the setpoint in the low byte + goto setbyte3 ;Clear the high byte + +;Indicate under which conditions the remote override setpoint can be overridden +;by the thermostat. The opentherm specs are quite confusing regarding which byte +;the flags must be in. So the gateway has an option to put them in both bytes. +MessageID100 btfss MsgResponse + return + btfsc OverrideFunc ;There is a new set of function bits + swapf RemOverrideFunc,F ;Start using the new function bits + bcf AlternativeUsed ;Allow user to override the response + call WordResponse + btfsc AlternativeUsed + goto UserOverride ;--- + call messageack ;Turn request into acknowledgement + swapf RemOverrideFunc,W + andlw b'11' ;Get the remote override flags + call setbyte4 + btfsc OverrideHigh + movfw byte4 ;Fix for confused manufacturers + call setbyte3 ;Reserved, must be 0? +UserOverride btfsc TStatISense ;Special treatment for iSense thermostat + btfss OverrideFunc ;First time after setpoint change? + goto OverrideFuncEnd + swapf RemOverrideFunc,W + xorwf RemOverrideFunc,W ;Compare the upper and lower nibbles + skpz + btfsc OverrideWait ;Func before setpoint + goto OverrideFuncEnd ;Previously sent fucntion bits are OK + bsf OverrideClr ;Clear the remote override setpoint + bsf OverrideReq ;Then set it again + bsf OverrideWait +OverrideFuncEnd bcf OverrideFunc ;Override func has been sent + return + +;The gateway implements version 3.0 of the opentherm protocol specification +MessageID125 btfss MsgResponse + return + call messageack ;Turn request into acknowledgement + movlw 3 + call setbyte3 ;Major version number + goto setbyte4 ;Minor version number + +;Check if work-arounds are needed for bugs in certain thermostats +MessageID126 btfss MsgResponse ;Only interested in requests + btfss TStatRemeha ;Only Remeha thermostats have known bugs + return + ;c-Mix: 17/17 + ;Celcia20: 20/3 + ;iSense: 30/19, 32/23 + bcf TStatISense ;Clear guess based on ID2 information + movfw byte3 ;Master device product version number + sublw 20 ;Celcia 20 + skpnz + bsf TStatCelcia + skpc + bsf TStatISense ;At least 30 and 32 are iSenses + return + +;Messages to read transparent slave parameters (TSPs) and fault history buffer +;(FHB) entries. +TSPBufferSize btfss MsgResponse ;Only interested in responses + return + btfsc AlternativeUsed ;Info was not requested by the gateway + btfss BoilerResponse ;Response from the boiler + goto WordResponse ;Response going back to the thermostat + movfw byte3 ;Get the number of entries + btfss byte1,5 ;Check for valid response + skpnz + return ;No (valid) data available + movwf TSPCount ;Store the number of entries + incf byte2,W ;Reading entries is the next MsgID + movwf prioritymsgid ;Start reading entries + bsf PriorityMsg + return + +TSPReadEntry btfsc MsgResponse ;Only interested in responses ... + btfss AlternativeUsed ;... not requested by the thermostat + goto ByteResponse + incf TSPIndex,F ;Index of next entry + movfw TSPCount + subwf TSPIndex,W ;Check if more entries exist + skpc + bsf PriorityMsg ;Prepare to read next entry + return + +;Remove blacklisting for special DataIDs. Do this when receiving a request, so +;even (accidental) manual action cannot blacklist the DataID. +MandatoryID btfsc MsgResponse ;Only interested in requests + return + call UnknownMask ;Get mask and pointer into unknownmap + xorlw -1 ;Invert the mask + andwf INDF,F ;Unmark the ID as unsupported + return + +Alternative movlw T_READ ;Alternative is probably a read command + movwf byte1 + clrf byte3 ;And a data value of 0 + clrf byte4 + tstf initflags + skpnz + goto GotConfigData + ;Obtain some parameters from the boiler that are useful for the + ;internal operation of the gateway. + bsf NoResetUnknown ;Don't whitelist if command succeeds + btfsc InitParameter + retlw MSG_REMOTEPARAM + btfsc InitHotWater + retlw MSG_DHWBOUNDS + btfsc InitHeating + retlw MSG_MAXCHBOUNDS + btfsc WaterSetpoint + goto SetWaterSet + btfsc MaxHeatSetpoint + goto SetHeatingSet + btfss PriorityMsg + goto InitDone + movfw TSPIndex + movwf byte3 + movfw prioritymsgid + return +InitDone clrf initflags +GotConfigData movfw resetflags ;Check for pending reset requests + skpz + goto ResetCommand ;Send a reset command + btfsc NoAlternative + goto RestoreRequest ;No alternatives available, W = 0 + movlw 32 ;Size of the list of alternatives + movwf loopcounter ;Counter to prevent infinite loop +AltLoop incf alternativecmd,W + andlw b'11111' + movwf alternativecmd ;Next entry in the list of alternatives + addlw AlternativeCmd ;Add the starting address of the list + pcall EepromWait ;Wait for any pending EEPROM activity + bcf STATUS,RP0 + movwf EEADR ;Load the list pointer + bsf STATUS,RP0 + bsf EECON1,RD ;Prepare for reading EEPROM data + bcf STATUS,RP0 + movfw EEDATA ;Get the alternative message ID + bcf STATUS,RP1 ;Bank 0 + skpz ;Check if the slot is empty + return ;Found an entry + decfsz loopcounter,F + goto AltLoop ;Continue searching + bsf NoAlternative ;List of alternatives is empty +RestoreRequest bcf AlternativeUsed ;Not providing an alternative after all + ;At this point W = 0 +CreateMessage iorwf originaltype,W ;Combine W and the original message type + movwf byte1 ;Set the message type for the response + movfw databyte1 + movwf byte3 ;Restore data byte #1 + movfw databyte2 + movwf byte4 ;Restore data byte #2 + movfw originalreq + movwf byte2 ;Restore the message ID + return + +ResetCommand movlw T_WRITE + movwf byte1 ;Initialize a Write-Data message + movlw 116 ;MsgID of first counter + movwf loopcounter ;Initialize loopcounter + clrf temp ;Use for mask + setc ;Make sure to shift in a '1' + goto ResetCmdJump +ResetCmdLoop incf loopcounter,F +ResetCmdJump rlf temp,F ;Shift the mask one position + movfw temp ;Get the mask + andwf resetflags,W ;Check if the matching resetflag is set + skpnz + goto ResetCmdLoop ;Find the first set bit + xorwf resetflags,F ;Clear the resetflag + movfw loopcounter ;Get the Message ID + return + +UnknownMask movfw byte2 +UnknownMask2 movwf temp2 + rrf temp2,W + movwf temp + rrf temp,W + andlw b'00011111' ;Get bit 2-7 + addlw unknownmap + movwf FSR ;Pointer into the map of unknown ID's + btfsc temp2,7 ;Check for msg ID's in the range 0..127 + retlw 0 ;Don't blacklist vendor specific ID's + movlw b'11' + btfsc temp2,0 + movlw b'1100' + movwf temp + btfsc temp2,1 + swapf temp,F ;Calculate mask for the desired bit + comf INDF,W + andwf temp,W ;Check if ID is unsupported by boiler + swapf temp,F + swapf temp,W ;Get the mask without changing the Z bit + return ;Z = 1 if message is not supported + +;************************************************************************ +;Values to store +;Byte 0 : (00) 0 1 6 01000011 +;Byte 1 : (03) 14 15 11000000 +;Byte 2 : (05) 16 17 18 00000111 +;Byte 3 : (08) 24 25 26 27 28 00011111 +;Byte 4 : 00000000 +;Byte 5 : 00000000 +;Byte 6 : (0D) 48 49 00000011 +;Byte 7 : (0F) 56 57 00000011 +;Byte 8 : 00000000 +;Byte 9 : 00000000 +;Byte 10: 00000000 +;Byte 11: 00000000 +;Byte 12: 00000000 +;Byte 13: 00000000 +;Byte 14: (11) 116 117 118 119 11110000 +;Byte 15: (15) 120 121 122 123 00001111 +;Keep the three tables in sync! + +#define outsideval1 valuestorage + 22 +#define outsideval2 valuestorage + 23 + +StoreValue rlf byte2,W + skpc ;Ignore manufacturer specific ID's + btfsc byte1,5 ;Only store valid data + return + movwf temp + swapf temp,W + andlw b'00001111' ;Calculate table entry + movwf temp + movfw byte2 + andlw b'00000111' + movwf loopcounter + incf loopcounter,F + call LookupStoreFlag + xorwf temp,W ;Exchange W and temp + xorwf temp,F + xorwf temp,W ;-- + clrf tempvar0 + clrc ;Carry may be set in LookupStoreFlag +StoreValueLoop skpnc + incf tempvar0,F + rrf temp,F + decfsz loopcounter,F + goto StoreValueLoop + skpc + return ;Not storing this ID + call LookupStoreOffs + addwf tempvar0,F + rlf tempvar0,W + addlw valuestorage + movwf FSR + bsf STATUS,IRP + movfw byte3 + movwf INDF + incf FSR,F + movfw byte4 + movwf INDF + bcf STATUS,IRP + return + +LookupStoreFlag movlw high StoreFlagTable + movwf PCLATH + movfw temp + addlw low StoreFlagTable + skpnc + incf PCLATH,F + movwf PCL +StoreFlagTable dt b'01000011' + dt b'11000000' + dt b'00000111' + dt b'00011111' + dt b'00000000' + dt b'00000000' + dt b'00000011' + dt b'00000011' + dt b'00000000' + dt b'00000000' + dt b'00000000' + dt b'00000000' + dt b'00000000' + dt b'00000000' + dt b'11110000' + dt b'00001111' + +LookupStoreOffs movwf temp + movlw high StoreOffsTable + movwf PCLATH + movfw temp + addlw low StoreOffsTable + skpnc + incf PCLATH,F + movwf PCL +StoreOffsTable dt 0 + dt 3 + dt 5 + dt 8 + dt 13 + dt 13 + dt 13 + dt 15 + dt 17 + dt 17 + dt 17 + dt 17 + dt 17 + dt 17 + dt 17 + dt 21 + constant SUMMARYFIELDS=25 ;Maximum: 48 + +;The following code should only run for responses going back to the thermostat. +;A response to an alternative message coming back from the boiler (which will +;not be sent to the thermostat) can be recognized by both BoilerResponse and +;AlternativeUsed being set. +;The clever combination of bit tests in the following code will end up at a +;return command for requests or alternative responses. +FakeResponse btfsc BoilerResponse + btfss AlternativeUsed + btfss MsgResponse + return ;Not a message towards the thermostat + goto ClearNoneResp +ByteResponse btfsc BoilerResponse + btfss AlternativeUsed + btfss MsgResponse + return ;Not a message towards the thermostat + goto ClearByteResp +MandatoryWord call MandatoryID ;Prevent the DataID getting blacklisted +WordResponse btfsc BoilerResponse + btfss AlternativeUsed + btfss MsgResponse + return ;Not a message towards the thermostat +ClearWordResp clrf databyte1 +ClearByteResp clrf databyte2 +ClearNoneResp movfw byte2 ;Get the DataID + pcall FindResponse + skpz + return ;No entry found for the DataID + call messageack + bsf STATUS,IRP ;Access memory bank 2&3 + movfw INDF ;Get databyte 2 + iorwf databyte2,W + call setbyte4 + bcf FSR,7 + movfw INDF ;Get databyte 1 + bcf STATUS,IRP ;Access memory bank 0&1 + iorwf databyte1,W + goto setbyte3 + +;************************************************************************ +; Switch LEDs on and off depending on their configured function +;************************************************************************ + +SwitchLED skpnz ;Zero bit indicates LED state + goto SwitchOffLED + +SwitchOnLED addlw functions - 'A' ;Point into the table of functions + movwf FSR ;Setup indirect addressing + bsf INDF,0 ;Remember that this function is on + movlw b'11111110' ;Mask off the state bit + andwf INDF,W ;Get the LED(s) for the function + skpnz + return ;No LED is assigned to this function + xorlw -1 ;Invert the bitmask + andwf PORTB,F ;Switch on the LED + iorlw ~b'110' + andwf gpioflags,F ;Switch on virtual LEDs E/F + retlw 0 + +SwitchOffLED addlw functions - 'A' ;Point into the table of functions + movwf FSR ;Setup indirect addressing + bcf INDF,0 ;Remember that this function is off + movfw INDF ;Get the LED for the function + skpnz + return ;No LED is assigned to this function + iorwf PORTB,F ;Switch off the LED + andlw b'110' + iorwf gpioflags,F ;Switch off virtual LEDs E/F + retlw 0 + +;************************************************************************ +; Parse commands received on the serial interface +;************************************************************************ + + package Serial +SerialCommand btfsc Overrun + goto SerialOverrun +SerialCmdSub movlw 4 + subwf rxpointer,W ;Check the length of the command >= 4 + skpc + retlw SyntaxError ;Invalid command + movfw rxbuffer + 2 ;Third character of every command ... + sublw '=' ;... must be '=' + skpz + retlw SyntaxError + movlw high SerialCmdTable + movwf PCLATH + movfw rxbuffer ;Get the first character of the command + xorwf rxbuffer + 1,W ;Combine with the second character + movwf temp ;Save for later use + andlw ~b'11111' ;First quick check for a valid command + skpz + retlw CommandNG ;Command is not valid + ;Report the command back to the caller + movfw rxbuffer + lcall PrintChar + movfw rxbuffer + 1 + call PrintChar + movlw ':' + call PrintChar + movlw ' ' + call PrintChar + ;Indexed jump on the exclusive or of the 2 command characters + movlw high SerialCmdTable + movwf PCLATH + movfw temp + addlw low SerialCmdTable + skpnc + incf PCLATH,F + movwf PCL ;Make a quick initial selection +; Calculate table index with Tcl: tcl::mathop::^ {*}[scan SB %c%c] +SerialCmdTable goto SerialCmd00 ; AA, MM, TT commands + goto SerialCmd01 ; RS, SR commands + goto SerialCmd02 ; PR, KI commands + goto SerialCmd03 ; PS command + goto SerialCmd04 ; VR, SW commands + goto SerialCmd05 ; DA, GB, VS commands + goto SerialCmdGPIO ; GA command + goto SerialCmd07 ; OH command + goto SerialCmdLED ; LD command + goto SerialCmdLED ; LE command + goto SerialCmdLED ; LF command + goto SerialCmd0B ; CH command + retlw CommandNG +#ifndef LVP + goto SerialCmdLED ; LA command +#else + retlw CommandNG +#endif + goto SerialCmdLED ; LB command + goto SerialCmdLED ; LC command + goto SerialCmd10 ; GW, SC, CS commands + goto SerialCmd11 ; CR, SB commands + goto SerialCmd12 ; FT command + retlw CommandNG + goto SerialCmd14 ; DP command + retlw CommandNG + retlw CommandNG + goto SerialCmd17 ; TC command + retlw CommandNG + retlw CommandNG + retlw CommandNG + goto SerialCmd1B ; OT, SH commands + goto SerialCmd1C ; UI command + goto SerialCmd1D ; IT, PM commands + retlw CommandNG + goto SerialCmd1F ; HW command + +SerialOverrun bcf Overrun + retlw OverrunError + +SerialCmd00 movfw rxbuffer + xorlw 'T' + skpnz + goto SetTempSetPoint + xorlw 'T' ^ 'A' + skpnz + goto SetAlternative + xorlw 'A' ^ 'M' + skpnz + goto SetMaxModLevel + retlw CommandNG + +SerialCmd01 movfw rxbuffer + xorlw 'R' + skpnz + goto ResetCounter + xorlw 'R' ^ 'S' + skpnz + goto SetResponse + retlw CommandNG + +SerialCmd02 movfw rxbuffer + xorlw 'P' + skpnz + goto ReportSetting + xorlw 'P' ^ 'K' + skpnz + goto KnownDataID + retlw CommandNG + +SerialCmd03 movfw rxbuffer + xorlw 'P' + skpnz + goto ReportSummary + retlw CommandNG + +SerialCmd04 movfw rxbuffer + xorlw 'V' + skpnz + goto SetVoltageRef + xorlw 'V' ^ 'S' + skpnz + goto SetHotWaterTemp + retlw CommandNG + +SerialCmd05 movfw rxbuffer + xorlw 'D' + skpnz + goto DelAlternative + xorlw 'D' ^ 'V' + skpnz + goto SetVentSetpoint + goto SerialCmdGPIO + +SerialCmd07 movfw rxbuffer + xorlw 'O' + skpnz + goto SetOverrideHigh + retlw CommandNG + +SerialCmd0B movfw rxbuffer + xorlw 'C' + skpnz + goto SetCentralHeat + retlw CommandNG + +SerialCmd10 movfw rxbuffer + xorlw 'S' + skpnz + goto SetClock + xorlw 'S' ^ 'G' + skpnz + goto SetGatewayMode + xorlw 'G' ^ 'C' + skpnz + goto SetCtrlSetpoint + retlw CommandNG + +SerialCmd11 movfw rxbuffer + xorlw 'C' + skpnz + goto ClearResponse + xorlw 'C' ^ 'S' + skpnz + goto SetSetBack + retlw CommandNG + +SerialCmd12 movfw rxbuffer + xorlw 'F' + skpnz + goto SetTStatModel + retlw CommandNG + +SerialCmd14 movfw rxbuffer + xorlw 'D' + skpnz + goto SetDebugPtr + retlw CommandNG + +SerialCmd17 movfw rxbuffer + xorlw 'T' + skpnz + goto SetContSetPoint + retlw CommandNG + +SerialCmd1B movfw rxbuffer + xorlw 'O' + skpnz + goto SetOutsideT + xorlw 'O' ^ 'S' + skpnz + goto SetHeatingTemp + retlw CommandNG + +SerialCmd1C movfw rxbuffer + xorlw 'U' + skpnz + goto UnknownDataID + retlw CommandNG + +SerialCmd1D movfw rxbuffer + xorlw 'I' + skpnz + goto IgnoreError1 + xorlw 'I' ^ 'P' + skpnz + goto SetPrioMessage + retlw CommandNG + +SerialCmd1F movfw rxbuffer + xorlw 'H' + skpnz + goto SetHotWater + retlw CommandNG + +SerialCmdLED movfw rxbuffer + xorlw 'L' + skpz + retlw CommandNG ;LED commands must start with 'L' + movfw rxpointer + sublw 4 + skpz + retlw SyntaxError ;Command length must be 4 + movfw rxbuffer + 3 + movwf temp ;Keep func code for storing in EEPROM + sublw 'Z' + sublw 'Z' - 'A' ;Check for valid function code + skpc + retlw BadValue ;Valid functions are 'A' - 'Z' + movlw 'A' + subwf rxbuffer + 1,W ;Get LED number + addlw FunctionLED1 ;Calculate EEPROM address + call WriteEpromData ;Save the LED configuration in EEPROM + movfw rxbuffer + 3 + pcall PrintChar + movlw 'A' + subwf rxbuffer + 1,W ;Get LED number again + movwf temp + movfw rxbuffer + 3 ;Get the function code +SetLEDFunction movwf float1 ;Save temporarily + movlw b'00001000' ;Bit mask for LED 0 + btfsc temp,1 + movlw b'01000000' ;Bit mask for LED 2 + btfsc temp,2 + movlw b'00000010' ;Bit mask for LED 4 + movwf tempvar0 ;Save the bit mask + clrc + btfsc temp,0 + rlf tempvar0,F ;Shift the bit mask for odd LEDs + ;Remove the old function for the LED + movlw functions ;Start of the function table + movwf FSR ;Setup indirect adressing + movlw 26 ;Size of the table + movwf loopcounter + comf tempvar0,W ;Invert the bit mask +SetLEDLoop andwf INDF,F ;Remove the LED from the function flags + incf FSR,F ;Point to the next function + decfsz loopcounter,F + goto SetLEDLoop ;Repeat the loop + ;Setup the new function for the LED + movfw float1 ;Name of the new function + addlw functions - 'A' ;Pointer into the function table + movwf FSR ;Setup indirect addressing + movfw tempvar0 ;Reload the bit mask for the LED + iorwf INDF,F ;Link the LED to the selected function + movfw float1 ;Name of the new function, clears Z-bit + btfss INDF,0 ;Check the function state + setz ;Function state is off + lgoto SwitchLED ;Set the initial state of the LED + +SerialCmdGPIO movfw rxbuffer + sublw 'G' + skpz + retlw CommandNG ;GPIO commands must start with 'G' + movfw rxpointer + sublw 4 + skpz + retlw SyntaxError ;Command length must be 4 + movlw '0' + subwf rxbuffer + 3,W + movwf temp ;Keep func code for storing in EEPROM + andlw b'11111000' ;Currently there are 7 GPIO functions + skpz + retlw SyntaxError ;Invalid GPIO function + movlw b'11110000' ;Mask to clear old GPIO A function + btfsc rxbuffer + 1,0 ;Configuring GPIO B? + goto SetGPIOFunction + andwf GPIOFunction,W ;GPIO B function code in high nibble + sublw 0x70 ;Old GPIO B function was DS1820? + skpnz + bcf OutsideTemp ;Forget outside temperature + swapf temp,F ;Move new function code to upper nibble + movlw b'1111' ;Mask to clear old GPIO B function +SetGPIOFunction andwf GPIOFunction,W ;Clear the old function code + iorwf temp,W ;Insert the new function code + movwf GPIOFunction ;Store in RAM + movwf temp ;Value to write to EEPROM + movlw FunctionGPIO ;EEPROM address to write to + call WriteEpromData ;Save in EEPROM + bsf gpio_port1 + bcf gpio_port2 + movlw b'11000000' + btfss rxbuffer + 1,0 ;Configuring GPIO A? + xorwf gpio_mask,F + lcall gpio_initport + movfw GPIOFunction + btfss rxbuffer + 1,0 ;Configuring GPIO A? + swapf GPIOFunction,W + andlw b'111' +GoPrintDigit lgoto PrintDigit + +CheckBoolean clrc ;Carry indicates success + movfw rxpointer + xorlw 4 ;Check the command is 4 characters long + skpz + retlw SyntaxError ;Wrong length + movfw rxbuffer + 3 ;Get the flag state + sublw '1' ;It must be either '0' or '1' + sublw '1' - '0' ;Only the two allowed values ... + skpnc ;... result in a carry + return ;Success + retlw BadValue ;Illegal boolean value + +SetContSetPoint call GetFloatArg ;Parse floating point value + skpnc + return + movlw b'01' ;Only allow manual override + goto SetSerSetPoint +SetTempSetPoint call GetFloatArg ;Parse floating point value + skpnc + return + movlw b'11' ;Allow manual and program override +SetSerSetPoint btfsc NegativeTemp ;Check for a negative value + retlw SyntaxError ;Cannot set the thermostat below zero + call SetSetPoint ;Change the setpoint + goto CommandFloat ;Confirm the new setpoint + +SetSetPoint xorwf RemOverrideFunc,W ;Store function bits in lower nibble + andlw b'1111' + xorwf RemOverrideFunc,F ;-- + movlw 0 ;Clear all old state information + btfss TStatISense ;Special treatment for iSense thermostat + goto SetSetPointJ1 + movfw setpoint1 ;Check old setpoint + iorwf setpoint2,W + skpz ;There is no old setpoint + bsf OverrideClr ;Clear old setpoint before setting new + movlw 1 << 4 ;Leave OverrideClr bit intact +SetSetPointJ1 andwf override,F ;Clear old state information + movfw float2 ;Copy the value to the setpoint vars + movwf setpoint2 + movfw float1 + movwf setpoint1 ;-- + bsf OverrideReq ;Start blinking the override LED + bsf OverrideWait + bsf OverrideFunc + clrf celciasetpoint + btfsc TStatCelcia + bsf Unsolicited ;Send an unsolicited 04-frame + iorwf setpoint2,W ;Check if both bytes are 0 + skpnz + goto ClearSetPoint + btfss TStatCelcia ;Special treatment for Celcia thermostat + return +;The Remeha Celcia 20 thermostat does not react propperly to the value provided +;in a remote room setpoint override (MsgID=09). It needs to receive a remote +;command response (MsgID=04) to manipulate the room setpoint. +;Thanks to Rien Groot for figuring out this feature and providing the necessary +;information to add this option to the opentherm gateway firmware. +CelciaValue movfw setpoint1 + call Multiply5 ;Multiply the units by 5 + movfw float2 + movwf celciasetpoint + movfw setpoint2 + call Multiply5 ;Multiply the fraction by 5 + movfw float1 + addwf celciasetpoint,F;Add the two together + btfsc float2,7 + incf celciasetpoint,F;Round up + movfw setpoint1 + movwf float1 + movfw setpoint2 + movwf float2 + return +ClearSetPoint bsf OverrideClr ;Override request to be cancelled + bcf ManChangePrio + bcf ProgChangePrio + return + +SetSetBack call GetFloatArg ;Parse floating point value + skpnc + return + iorwf float2,W + skpz ;Zero has special meaning in the code + btfsc NegativeTemp ;Check for a negative value + retlw SyntaxError ;Setpoint cannot be zero or below + movlw AwaySetpoint1 ;EEPROM address for units + call WriteEpromData ;Save in EEPROM + movfw float2 + movwf temp + movlw AwaySetpoint2 ;EEPROM address for fraction + call WriteEpromData ;Save in EEPROM + goto CommandFloat + +SetHotWaterTemp call GetFloatArg + skpnc + return + iorwf float2,W + skpnz + goto HotWaterReset + movfw dhwsetpointmin + call Compare + skpc + retlw BadValue + movfw dhwsetpointmax + call Compare + skpz + skpc + goto HotWaterTempOK + retlw BadValue +HotWaterReset bcf WaterSetpoint + goto HotWaterFinish +HotWaterTempOK btfsc dhwupdate ;Check if boiler supports write access + bsf WaterSetpoint ;Schedule to send command to the boiler +HotWaterFinish movfw float2 + movwf dhwsetpoint2 + movfw float1 + movwf dhwsetpoint1 + goto CommandFloat + +SetHeatingTemp call GetFloatArg + skpnc + return + iorwf float2,W + skpnz + goto HeatingReset + movfw chsetpointmin + call Compare + skpc + retlw BadValue + movfw chsetpointmax + call Compare + skpz + skpc + goto HeatingTempOK + retlw BadValue +HeatingReset bcf MaxHeatSetpoint + goto HeatingFinish +HeatingTempOK btfsc maxchupdate ;Check if boiler supports write access + bsf MaxHeatSetpoint ;Schedule to send command to the boiler +HeatingFinish movfw float2 + movwf maxchsetpoint2 + movfw float1 + movwf maxchsetpoint1 + goto CommandFloat + +SetCtrlSetpoint call GetFloatArg + skpnc + return + btfsc NegativeTemp ;Check for a negative value + retlw SyntaxError ;Negative temperatures are not allowed + movwf controlsetpt1 + movfw float2 + movwf controlsetpt2 + goto CommandFloat + +SetVentSetpoint call GetDecimalArg ;Get the ventilation setpoint + skpnc + goto ClrVentSetpoint ;Non-numeric value + tstf INDF + skpz + retlw SyntaxError ;Characters following the value + sublw 100 + skpc + retlw OutOfRange ;Value must be in the range 0-100 + rlf temp,W ;Shift a 1 into bit 0, value in bit 1-7 + movwf ventsetpoint + movfw temp + lgoto PrintByte ;Confirm the command +ClrVentSetpoint clrf ventsetpoint + goto ValueCleared + +SetOutsideT call GetFloatArg + skpnc + return + btfss float1,7 ;Temperature value negative? + btfss float1,6 ;Temperature higher than 64 degrees? + goto StoreOutsideT + bcf OutsideTemp ;No (reasonable) outside temperature +ValueCleared movlw '-' + lgoto PrintChar +StoreOutsideT call StoreOutTemp +CommandFloat lgoto PrintSigned + +StoreOutTemp movfw float1 + movwf outside1 + bsf STATUS,RP1 + movwf outsideval1 + bcf STATUS,RP1 + movfw float2 + movwf outside2 + bsf STATUS,RP1 + movwf outsideval2 + bcf STATUS,RP1 + bsf OutsideTemp + bcf OutsideInvalid ;Outside temperature is valid + return + +SetDebugPtr movfw rxpointer + sublw 5 + skpz + retlw SyntaxError + call GetHexArg + skpnc + return + movwf debug + lgoto PrintHex + +SetClock call GetDecimalArg ;Get the hours + skpnc + return + movwf clock1 + sublw 23 + skpc + retlw OutOfRange + + movfw INDF + sublw ':' + skpz + retlw SyntaxError + incf FSR,F + + call GetDecimal ;Get the minutes + skpnc + retlw SyntaxError + movwf clock2 + sublw 59 + skpc + retlw OutOfRange + + movfw INDF + sublw '/' + skpz + retlw SyntaxError + incf FSR,F + + movlw '0' ;Get the day of the week + subwf INDF,W + skpz + skpc + retlw BadValue + movwf temp + andlw b'11111000' + skpz + retlw BadValue + incf FSR,F + tstf INDF + skpz + retlw SyntaxError + swapf temp,F + clrc + rlf temp,W + iorwf clock1,F + movlw 120 | 1 << 7 + movwf minutetimer + movfw clock1 + andlw b'11111' + movwf temp + lcall PrintDecimal + movfw clock2 + movwf temp + movlw ':' + call PrintFraction + movlw '/' + call PrintChar + swapf clock1,W + movwf temp + rrf temp,W + andlw b'111' + goto PrintDigit + +SetHotWater movfw rxpointer + sublw 4 + skpz + retlw SyntaxError + bcf HotWaterSwitch + bcf HotWaterEnable + movfw rxbuffer + 3 + sublw '1' + skpnz + bsf HotWaterEnable + andlw ~1 + skpnz + bsf HotWaterSwitch + lgoto PrintHotWater + +SetCentralHeat call CheckBoolean + skpc + return + skpz + bcf CHModeOff + skpnz + bsf CHModeOff + goto GoPrintDigit + +;Set the reference voltage: +;0 = 0.625V 1 = 0.833V 2 = 1.042V 3 = 1.250V 4 = 1.458V +;5 = 1.667V 6 = 1.875V 7 = 2.083V 8 = 2.292V 9 = 2.500V +SetVoltageRef movfw rxpointer + sublw 4 + skpz + retlw SyntaxError + movfw rxbuffer + 3 ;Get the reference voltage setting + sublw '9' ;Valid values are '0' through '9' + sublw '9' - '0' ;Convert the value, while at the ... + skpc ;... same time checking for valid input + retlw BadValue + addlw 3 + movwf temp + iorlw b'11100000' + bsf STATUS,RP0 + movwf CVRCON + bcf STATUS,RP0 + movlw b'11100000' + andwf settings,W + iorwf temp,W + movwf settings + call SaveSettings + lgoto PrintRefVoltage + +SaveSettings movwf temp + movlw SavedSettings +;Write to data EEPROM. Data must be in temp, address in W +WriteEpromData pcall EepromWait ;Wait for any pending EEPROM activity + bcf STATUS,RP0 + movwf EEADR ;Setup the EEPROM data address + movfw temp ;Get the value to store in EEPROM +StoreEpromData bsf STATUS,RP1 + bsf STATUS,RP0 + bsf EECON1,RD ;Read the current value + bcf STATUS,RP0 + xorwf EEDATA,W ;Check if the write can be skipped + skpnz + goto StoreEpromSkip ;Prevent unnecessary EEPROM writes + xorwf EEDATA,F ;Load the data value + bsf STATUS,RP0 + bsf EECON1,WREN ;Enable EEPROM writes + bcf INTCON,GIE ;The sequence should not be interrupted + ;Required sequence to write EEPROM + movlw 0x55 + movwf EECON2 + movlw 0xAA + movwf EECON2 + bsf EECON1,WR ;Start the actual write + ;-- + bsf INTCON,GIE ;Interrupts are allowed again + bcf EECON1,WREN ;Prevent accidental writes + bcf STATUS,RP0 +StoreEpromSkip bcf STATUS,RP1 ;Switch back to bank 0 + movfw temp ;Return the byte that was written + return + +SetAlternative call GetDecimalArg ;Get the message number + skpnc ;Valid decimal value? + return ;Return the error code from GetDecimal + skpnz ;Don't allow adding message ID=0 ... + retlw OutOfRange ;... because 0 indicates an empty slot + tstf INDF ;Is this the end of the command? + skpz + retlw SyntaxError + bcf NoAlternative ;There now is at least one alternative + pcall EepromWait ;Wait for any pending EEPROM activity + bcf STATUS,RP0 + movlw AlternativeCmd + movwf EEADR ;Setup access to list of alternatives + movfw temp ;Get DataID specified by the user +SetAltLoop bsf STATUS,RP0 + bsf EECON1,RD + bcf STATUS,RP0 + tstf EEDATA ;Check for an empty slot + skpnz + goto StoreAlt ;Slot is empty, put the DataID in it + incf EEADR,F ;Slot not empty, try next position + skpz ;End of list reached? + goto SetAltLoop + bcf STATUS,RP1 + retlw NoSpaceLeft ;No empty slot available +StoreAlt call StoreEpromData + lgoto PrintByte + +DelAlternative call GetDecimalArg ;Get the message number + skpnc ;Valid decimal value? + return ;Return the error code from GetDecimal + skpnz ;Don't allow deleting message ID=0 ... + retlw OutOfRange ;... because 0 indicates an empty slot + tstf INDF ;Is this the end of the command? + skpz + retlw SyntaxError + call DeleteAlt + skpnc ;MessageID was actually deleted? + return ;Return the error code + lgoto PrintByte ;Print the message ID + +DeleteAlt movwf temp + pcall EepromWait ;Wait for any pending EEPROM activity + bcf STATUS,RP0 + movlw AlternativeCmd ;Start of list of alternatives + movwf EEADR +DelAltLoop bsf STATUS,RP0 + bsf EECON1,RD ;Read a byte from EEPROM + bcf STATUS,RP0 + movfw temp + xorwf EEDATA,W ;Compare to the message ID to delete + skpnz + goto StoreEpromData ;Release the slot by writing 0 to it + incf EEADR,F ;Proceed to the next slot + skpz ;Check if we're done + goto DelAltLoop ;Repeat the loop + bcf STATUS,RP1 ;Switch back to bank 0 + setc ;Nothing was found + retlw NotFound ;The requested message ID was not found + +SetTStatModel movfw rxpointer + sublw 4 ;Command takes a single char argument + skpz + retlw SyntaxError + clrf remehaflags ;Assume auto-detect + movfw rxbuffer + 3 + xorlw 'C' ;C=Celcia 20 + skpnz + bsf TStatCelcia + xorlw 'C' ^ 'I' ;I=ISense + skpnz + bsf TStatISense + tstf remehaflags ;No set bits means auto-detection + skpz + bsf TStatManual ;Thermostat model set manually + movfw remehaflags + movwf temp + movlw TStatModel ;EEPROM address for thermostat model + call WriteEpromData ;Save in EEPROM + lgoto PrintRemeha ;Report the selected model + +SetGatewayMode movfw rxpointer ;Check the command is 4 characters long + sublw 4 + skpz + retlw SyntaxError + call CheckBoolean + skpc + goto ResetGateway + xorwf mode,W ;Compare against the current setting + andlw 1 << 0 + skpz + goto SetModeDone ;Not really changing the mode + bsf ChangeMode ;Remember to change the gateway mode + call SetMonitorMode + btfss ChangeMode ;Mode change still pending? +SetModeDone movfw rxbuffer + 3 + lgoto PrintChar + +SetMonitorMode btfsc T2CON,TMR2ON ;Check if timer 2 is running + retlw 'P' ;Don't change while communicating + movlw 1 << 0 ;Mask for the MonitorMode flag + xorwf mode,F ;Toggle gateway/monitor mode + movlw b'00000011' ;Comparator setting for gateway mode + btfsc MonitorMode ;Check which mode to switch to + movlw b'00000110' ;Comparator setting for monitor mode + bsf STATUS,RP0 + movwf CMCON ;Configure the comparators + bcf STATUS,RP0 + bcf ChangeMode ;Change has been applied + return + +ResetGateway movfw rxbuffer + 3 + sublw 'R' ;GW=R resets the gateway + skpz + retlw BadValue + bsf resetreason,0 ;Resetting due to a serial command + bcf RSET ;Prepare the output latch + bsf STATUS,RP0 + bcf RSET ;Switch the pin to output + lgoto 0 ;Simulate reset + +SetMaxModLevel call GetDecimalArg ;Get the modulation level + skpnc + goto ClrMaxModLevel + tstf INDF + skpz + retlw SyntaxError + sublw 100 + skpc + retlw OutOfRange + bsf UserMaxModLevel + sublw 100 + movwf MaxModLevel + lgoto PrintByte +ClrMaxModLevel bcf UserMaxModLevel + movlw 100 + movwf MaxModLevel + goto ValueCleared + +ReportSetting lgoto PrintSetting + +ReportSummary lgoto PrintSummary + +SetResponse call GetDecimalArg ;Get the DataID + skpnc + return ;No decimal value specified + skpnz ;DataID should not be 0 + retlw BadValue + movwf temp2 + movlw ':' + subwf INDF,W + skpz + retlw SyntaxError + incf FSR,F + clrf float1 + call GetDecimal ;Get byte value + skpnc + return ;Missing byte value + tstf INDF + skpnz + goto OneByteResponse + movwf float1 ;Save as upper byte + movlw ',' + subwf INDF,W + skpz + retlw SyntaxError + incf FSR,F + call GetDecimal ;Get the lower byte value + skpnc + return ;Missing lower byte value + tstf INDF ;Command should be finished here + skpz + retlw SyntaxError +OneByteResponse movwf float2 ;Save lower byte + movfw temp2 ;Get specified DataID + call FindResponse ;Check if it is already in the list + skpnz + goto UpdateResponse ;DataID found in the list + clrw + call FindResponse ;Find an empty slot + skpz + retlw NoSpaceLeft ;There is no empty slot +UpdateResponse bsf STATUS,IRP + movfw float2 ;Get the lower byte + movwf INDF ;Save in the slot matching the DataID + bcf FSR,7 + movfw float1 ;Get the upper byte + movwf INDF ;Save in the slot matching the DataID + bcf STATUS,IRP + bsf FSR,7 + movfw temp2 + movwf INDF ;Save the DataID + lcall PrintByte + movlw ':' + call PrintChar + goto PrintBytes + +ClearResponse call GetDecimalArg ;Get the DataID + skpnc + return ;No decimal value specified + skpnz ;DataID should not be 0 + retlw BadValue + tstf INDF + skpz + retlw SyntaxError + call FindResponse ;Find the slot for the DataID + skpz + retlw NotFound ;The DataID was not in the list + clrf INDF ;Release the slot + movfw temp + lgoto PrintByte + +FindResponse movwf temp ;Save the DataID to search for + movlw ResponseValues1 + movwf FSR +FindResLoop movfw INDF ;Get the DataID for the response + xorwf temp,W + skpnz ;Check if the slot matches + return ;DataID found (carry is cleared) + movlw 1 + addwf FSR,F + skpdc + goto FindResLoop + retlw 0 ;DataID not found (carry is set) + +UnknownDataID call GetDecimalArg ;Get the DataID + skpnc + return ;No decimal value specified + skpz ;DataID should not be 0 + movwf float1 + btfsc float1,7 ;Only allow MsgID's in range 1..127 + retlw BadValue + tstf INDF ;Is this the end of the command? + skpz + retlw SyntaxError + pcall UnknownMask2 ;Get mask and pointer into unknownmap + iorwf INDF,F ;Mark message as unsupported + movfw float1 + call BitMask + addlw UnknownFlags + pcall ReadEpromData + iorwf temp,W +StoreDataIdMask call StoreEpromData ;Update non-volatile storage + movfw float1 + lgoto PrintByte + +KnownDataID call GetDecimalArg ;Get the DataID + skpnc + return ;No decimal value specified + skpz ;DataID should not be 0 + movwf float1 + btfsc float1,7 ;Only allow MsgID's in range 1..127 + retlw BadValue + tstf INDF ;Is this the end of the command? + skpz + retlw SyntaxError + pcall UnknownMask2 ;Get mask and pointer into unknownmap + xorlw -1 ;Invert the mask + andwf INDF,F ;Mark message as supported + movfw float1 + call BitMask + addlw UnknownFlags + pcall ReadEpromData + comf temp,F + andwf temp,W + goto StoreDataIdMask + +BitMask clrc + movwf temp2 + movlw b'1' + btfsc temp2,1 + movlw b'100' + movwf temp + btfsc temp2,0 + rlf temp,F + btfsc temp2,2 + swapf temp,F + swapf temp2,F + rlf temp2,W + rlf temp2,W + andlw b'11111' + return + +;There have been reports that some equipment does not produce clean transitions +;between the two logical Opentherm signal levels. As a result the gateway may +;produce frequent Error 01 reports. By setting the IT flag, the requirement for +;a maximum of one mid-bit transition is no longer checked. +;When this flag is set, Error 01 will never occur. +IgnoreError1 call CheckBoolean + skpc + return + movlw 1 << 5 ;Mask for IgnoreErr1 +SetSetting btfsc rxbuffer + 3,0 + iorwf settings,F ;Set the option + xorlw -1 ;Invert the mas + btfss rxbuffer + 3,0 + andwf settings,F ;Clear the option + movfw settings + call SaveSettings ;Store the setting in EEPROM + movfw rxbuffer + 3 + lgoto PrintChar + +SetOverrideHigh call CheckBoolean + skpc + return + movlw 1 << 6 ;Mask for the OverrideHigh bit + goto SetSetting + +;Reset boiler counters +;Algorithm: +;First char: H = 0, W = 2 +;Second char: B = 0, P = 1 +;Third char: S = 0, H = 4 +;If bit 1 is set, xor bit 0 + +ResetCounter movfw rxpointer + sublw 6 + skpz + retlw SyntaxError + movlw 1 + movwf temp + movfw rxbuffer + 3 + xorlw 'H' + skpnz + goto ResetCntJump1 + xorlw 'H' ^ 'W' + skpz + retlw SyntaxError + movlw 4 + movwf temp + movlw 'B' ^ 'P' +ResetCntJump1 xorwf rxbuffer + 4,W + xorlw 'B' + skpnz + goto ResetCntJump2 + xorlw 'B' ^ 'P' + skpz + retlw SyntaxError + clrc + rlf temp,F +ResetCntJump2 movfw rxbuffer + 5 + xorlw 'S' + skpnz + goto ResetCntJump3 + xorlw 'S' ^ 'H' + skpz + retlw SyntaxError + swapf temp,F +ResetCntJump3 movfw temp + iorwf resetflags,F + movfw rxbuffer + 3 + lcall PrintChar + movfw rxbuffer + 4 + call PrintChar + movfw rxbuffer + 5 + goto PrintChar + +SetPrioMessage call GetDecimalArg ;Get the DataID + skpnc + return ;No decimal value specified + tstf INDF ;Is this the end of the command? + skpz + retlw SyntaxError + movwf prioritymsgid + bsf PriorityMsg + clrf TSPCount + clrf TSPIndex + lgoto PrintByte + +CmdArgPointer movlw rxbuffer + 3 + movwf FSR + return + +;Returns the decimal value found. Carry is set on error +GetDecimalArg call CmdArgPointer +GetDecimal movfw INDF + sublw '9' + sublw '9' - '0' + skpc + goto CarrySyntaxErr +GetDecLoop movwf temp + incf FSR,F + movfw INDF + sublw '9' + sublw '9' - '0' + skpc + goto GetDecReturn + movwf tempvar0 + movlw 26 + subwf temp,W + skpnc + retlw OutOfRange + movfw temp + call Multiply10 + addwf tempvar0,W + skpc + goto GetDecLoop + retlw OutOfRange +GetDecReturn movfw temp + return +CarrySyntaxErr setc + retlw SyntaxError + +GetHexArg call CmdArgPointer +GetHexadecimal call GetHexDigit + skpnc + return + movwf temp + swapf temp,F + call GetHexDigit + skpc + iorwf temp,W + return + +GetHexDigit movlw '0' + subwf INDF,W + skpc + goto CarrySyntaxErr + addlw -10 + skpc + goto GetHexDigitNum + andlw ~h'20' + addlw -7 + skpc + goto CarrySyntaxErr + addlw -6 + skpnc + retlw SyntaxError + addlw 6 +GetHexDigitNum addlw 10 + incf FSR,F + clrc + return + +GetFloatArg call CmdArgPointer +GetFloat bsf NegativeTemp ;Assume a negative value + movfw INDF + sublw '-' ;Check if the assumption is right + skpnz + goto GetSignedTemp + bcf NegativeTemp ;No minus sign. Must be positive then. + sublw '-' - '+' ;Check for a plus sign + skpnz ;No sign specified +GetSignedTemp incf FSR,F ;Skip the sign + call GetDecimal ;Get the integer part + skpnc ;Carry is set on error + return ;Return the error code from GetDecimal + clrf float1 ;Assume no fractional part + movfw INDF ;Check for end of string + skpnz + goto GetFloatValue ;No fractional part specified + sublw '.' + skpz + goto CarrySyntaxErr ;The only allowed character is a '.' + movlw b'000000111' ;Action encoding + movwf loopcounter +FloatFracLoop incf FSR,F + movfw INDF ;Check for end of string + skpnz + goto GetFloatValue ;End of fractional part + sublw '9' + sublw '9' - '0' ;Only digits are allowed at this point + skpc + goto CarrySyntaxErr ;Found something other than a digit + btfsc loopcounter,2 + call Multiply10 ;Multiply the digit by 10 + btfsc loopcounter,1 + addwf float1,F + clrc + rrf loopcounter,F + tstf loopcounter + skpnz + skpc + goto FloatFracLoop + addlw -5 + skpnc + incf float1,F + goto FloatFracLoop +GetFloatValue btfss NegativeTemp ;Check for negative values + goto GetFloatWrapUp + comf temp,F ;Invert the integer part + tstf float1 + skpnz + goto GetFloatNegInt + movfw float1 + sublw 100 + movwf float1 +GetFloatWrapUp clrf float2 + call Divide100 + tstf tempvar1 ;The honneywell thermostat always ... + skpz ;... rounds down, so if the result ... + addlw 1 ;... is not exact, round up + movwf float2 + movfw temp + movwf float1 + return ;Carry is guaranteed to be clear +GetFloatNegInt incf temp,F ;Add 1 to get negative value + goto GetFloatWrapUp + +Multiply5 clrf float1 + movwf float2 + call Multiply2 + call Multiply2 + addwf float2,F + skpnc + incf float1,F + return + +Multiply2 clrc + rlf float2,F + rlf float1,F + return + +Multiply10 movwf tempvar1 ;Store a copy in a temporary variable + addwf tempvar1,F ;Add again to get: value * 2(clears C) + rlf tempvar1,F ;Shift left gives: value * 4 + addwf tempvar1,F ;Add original value: value * 5 + rlf tempvar1,W ;Shift left for: value * 10 + return ;Return the result + +;Divide float1:float2 by 100 - result in W +Divide100 movlw 16 + movwf loopcounter + clrf tempvar0 + clrf tempvar1 +DivideLoop rlf float2,F + rlf float1,F + rlf tempvar1,F + movlw 100 + subwf tempvar1,W + skpnc + movwf tempvar1 + rlf tempvar0,F + decfsz loopcounter,F + goto DivideLoop + movfw tempvar0 + return + +;Compare the value in float1:float2 to W, sets C and Z bit. C=1 if float >= W +Compare subwf float1,W + skpz + return + tstf float2 + return + +;************************************************************************ +; Implement the GPIO port functions +;************************************************************************ + + package gpio + constant GPIO_NONE=0 + constant GPIO_GND=1 + constant GPIO_VCC=2 + constant GPIO_LEDE=3 + constant GPIO_LEDF=4 + constant GPIO_HOME=5 + constant GPIO_AWAY=6 + constant GPIO_DS1820=7 + +gpio_init bsf gpio_port1 ;Set up the port mask for gpio port 1 + bcf gpio_port2 + call gpio_initport ;Initialize the port funtion + bcf gpio_port1 ;Set up the port mask for gpio port 2 + bsf gpio_port2 +gpio_initport movlw high gpio_inittab ;Standard indexed jump code + movwf PCLATH + movfw GPIOFunction + btfsc gpio_port2 + swapf GPIOFunction,W + andlw b'111' ;Limit to 0-7 + addlw low gpio_inittab + skpnc + incf PCLATH,F + movwf PCL ;Jump into the jump table +gpio_inittab goto gpio_input ;GPIO_NONE + goto gpio_initgnd ;GPIO_GND + goto gpio_initvcc ;GPIO_VCC + goto gpio_initvcc ;GPIO_LEDE + goto gpio_initvcc ;GPIO_LEDF + goto gpio_input ;GPIO_HOME + goto gpio_input ;GPIO_AWAY + goto gpio_onewire ;GPIO_DS1820 +gpio_onewire movlw b'11110000' + btfss gpio_port2 ;Function is only supported on port 2 + andwf GPIOFunction,F ;Disable function on port 1 +gpio_input movfw gpio_mask ;Get the port mask + andlw b'11000000' ;Mask of the other bits + bsf STATUS,RP0 + iorwf TRISA,F ;Set the port to input + bcf STATUS,RP0 + return +gpio_output comf gpio_mask,W ;Get the inverted port mask + iorlw b'00111111' ;Set all other bits + bsf STATUS,RP0 + andwf TRISA,F ;Set the port to output + bcf STATUS,RP0 + return +gpio_initgnd call gpio_output ;Make the port an output + andwf PORTA,F ;Pull the output low + return +gpio_initvcc call gpio_output ;Make the port an output + xorlw -1 ;Invert the mask + iorwf PORTA,F ;Pull the output high + return + +gpio_common bsf gpio_port1 ;Set up the port mask for gpio port 1 + bcf gpio_port2 + call gpio_apply ;Perform the port function + bcf gpio_port1 ;Set up the port mask for gpio port 2 + bsf gpio_port2 +gpio_apply movlw high gpio_table ;Standard indexed jump code + movwf PCLATH + movfw GPIOFunction + btfsc gpio_port2 + swapf GPIOFunction,W + andlw b'111' ;Limit to 0-7 + addlw low gpio_table + skpnc + incf PCLATH,F + movwf PCL ;Jump into the jump table +gpio_table return ;GPIO_NONE + return ;GPIO_GND + return ;GPIO_VCC + goto gpio_lede ;GPIO_LEDE + goto gpio_ledf ;GPIO_LEDF + goto gpio_home ;GPIO_HOME + goto gpio_away ;GPIO_AWAY + goto gpio_temp ;GPIO_DS1820 +gpio_lede movfw PORTA ;Get current state of the GPIO pin + btfsc VirtualLedE ;Check the shadow LED state + comf PORTA,W ;Get inverted state of the GPIO pin + andwf gpio_mask,W ;Mask off the other GPIO pin + andlw b'11000000' ;Mask off the remaining bits + xorwf PORTA,F ;Update the GPIO port + return +gpio_ledf movfw PORTA ;Get current state of the GPIO pin + btfsc VirtualLedF ;Check the shadow LED state + comf PORTA,W ;Get inverted state of the GPIO pin + andwf gpio_mask,W ;Mask off the other GPIO pin + andlw b'11000000' ;Mask off the remaining bits + xorwf PORTA,F ;Update the GPIO port + return +gpio_temp btfss Intermission ;Wait for the gap between messages + return + tstf bitcount ;Check that no line events have happened + pcall Temperature ;Do one temperature measurement step + iorlw 0 ;Check if measurement is complete + skpnz + bcf Intermission ;No more steps to be done + return +gpio_home comf PORTA,W ;Get the inverted state of PORT A + goto gpio_security +gpio_away movfw PORTA ;Get the normal state of PORT A +gpio_security andwf gpio_mask,W ;Apply the GPIO port mask + andlw b'11000000' ;Clear the irrelevant bits + skpz + goto gpio_armed ;Security system is armed + btfss gpioaway ;Did the port change since last check? + return ;Nothing to do + bcf gpioaway ;Security system is disarmed + clrf float1 ;Cancel the remote setpoint + clrf float2 + goto gpio_setpoint ;W is 0 +gpio_armed btfsc gpioaway ;Did the port change since last check? + return ;Nothing to do + bsf gpioaway ;Security system is armed + movlw AwaySetpoint1 ;EEPROM address of the away setpoint + lcall ReadEpromData ;Get the setpoint from EEPROM + movwf float1 ;Configure the remote setpoint + movlw AwaySetpoint2 ;EEPROM address of the away setpoint + call ReadEpromData ;Get the setpoint from EEPROM + movwf float2 ;Configure the remote setpoint + movlw b'01' ;Temperature override mode: Continue +gpio_setpoint pcall SetSetPoint ;Set setpoint + return + +;######################################################################## + +;Initialize EEPROM data memory + + org 0x2100 +SavedSettings de 6 | 1 << 5 | 1 << 6 ;Set IgnoreErr1 and OverrideHigh +FunctionGPIO de GPIO_NONE | GPIO_NONE << 4 +AwaySetpoint1 de 16 +AwaySetpoint2 de 0 +PrintSettingL de "L=" +FunctionLED1 de 'M' +FunctionLED2 de 'X' +FunctionLED3 de 'O' +FunctionLED4 de 'F' +FunctionLED5 de 'P' +FunctionLED6 de 'C' +NullString de 0 +TStatModel de 0 + +PrintSettingA de "A=" +GreetingStr de "OpenTherm Gateway ", version +#ifdef __DEBUG + de phase, patch, ".", build +#else + #ifdef patch + de phase, patch + #ifdef bugfix + de '.', bugfix + #endif + #endif +#endif + de 0 +TimeoutStr de "WDT reset!", 0 +TDisconnect de "Thermostat disconnected", 0 +TConnect de "Thermostat connected", 0 +CommandNG de "NG", 0 +SyntaxError de "SE", 0 +NoSpaceLeft de "NS", 0 +BadValue de "BV", 0 +NotFound de "NF", 0 +OutOfRange de "OR", 0 +OverrunError de "OE", 0 +MediumPowerStr de "Medium", 0 +HighPowerStr de "High", 0 +NormalPowerStr de "Low" +PowerStr de " power", 0 +ErrorStr de "Error ", 0 +PrintSettingB de "B=" +TimeStamp de tstamp, 0 +PrintSettingC de "C=" +SpeedString de mhzstr, " MHz", 0 +ResetReasonStr de "ECSLPB" + + org 0x21D0 +UnknownFlags de 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 + org 0x21E0 +AlternativeCmd de 116,117,118,119,120,121,122,123 + de 0,0,0,0,0,0,0,0 + de 0,0,0,0,0,0,0,0 + de 0,0,0,0,0,0,0,0 + end diff --git a/boiler-monster/original-pic-4.2.5/license.txt b/boiler-monster/original-pic-4.2.5/license.txt new file mode 100644 index 0000000..6d576d3 --- /dev/null +++ b/boiler-monster/original-pic-4.2.5/license.txt @@ -0,0 +1,58 @@ +Copyright (c) 2009, Schelte Bron + +PREAMBLE + +This license establishes the terms under which a given software Package may +be copied, modified, distributed, and/or redistributed. The intent is that +the Copyright Holder maintains control over the development and distribution +of the Package, while allowing the users of the Package to use the Package +in a variety of ways. + +You are always permitted to make arrangements wholly outside of this license +directly with the Copyright Holder of a given Package. If the terms of this +license do not permit the full use that you propose to make of the Package, +you should contact the Copyright Holder and seek a different licensing +arrangement. + +DEFINITIONS + +"Copyright Holder" means the individual(s) or organization(s) named in the +copyright notice for the entire Package. + +"You" and "your" means any person who would like to copy, distribute, or +modify the Package. + +"Package" means the collection of files distributed by the Copyright Holder, +and derivatives of that collection and/or of those files. A given Package +may consist of either the Standard Version, or a Modified Version. + +"Distribute" means providing a copy of the Package or making it accessible +to anyone else, or in the case of a company or organization, to others +outside of your company or organization. + +"Standard Version" refers to the Package if it has not been modified, or has +been modified only in ways explicitly requested by the Copyright Holder. + +"Modified Version" means the Package, if it has been changed, and such +changes were not explicitly requested by the Copyright Holder. + +TERMS + +1. You may use this Package for non-commercial purposes without charge. + +2. You may make verbatim copies of this Package for personal use, provided +that you duplicate all of the original copyright notices and associated +disclaimers. You may not distribute copies of this Package, or copies of +packages derived from this Package, to others without specific prior written +permission from the Copyright Holder (although you are encouraged to direct +them to sources from which they may obtain it for themselves). + +3. You may apply bug fixes, portability fixes, and other modifications. A +Package modified in such a way shall still be covered by the terms of this +license. + +4. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED +WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL +THE AUTHORS BE HELD LIABLE FOR ANY DAMAGES ARISING FROM THE USE OF THIS +SOFTWARE. diff --git a/boiler-monster/original-pic-4.2.5/ot-gateway.brd b/boiler-monster/original-pic-4.2.5/ot-gateway.brd new file mode 100644 index 0000000..301dede Binary files /dev/null and b/boiler-monster/original-pic-4.2.5/ot-gateway.brd differ diff --git a/boiler-monster/original-pic-4.2.5/ot-gateway.sch b/boiler-monster/original-pic-4.2.5/ot-gateway.sch new file mode 100644 index 0000000..580fd38 Binary files /dev/null and b/boiler-monster/original-pic-4.2.5/ot-gateway.sch differ diff --git a/boiler-monster/original-pic-4.2.5/readme.txt b/boiler-monster/original-pic-4.2.5/readme.txt new file mode 100644 index 0000000..fd5d117 --- /dev/null +++ b/boiler-monster/original-pic-4.2.5/readme.txt @@ -0,0 +1,43 @@ +Requirements +============ + +The provided files are intended to be used together with the MPLAB tools that +can be downloaded for free form Microchip (www.microchip.com). The firmware +has been developed on MPLAB v8.53. + + +Compiling +========= + +The build.cmd script can be used to build the Opentherm Gateway firmware. If +you installed the MPLAB tools in a non-standard location, you will have to +modify the path on the second line of the script. + + +MPLAB Project +============= + +If you want to create a MPLAB IDE project for the Opentherm Gateway firmare, +you can follow these steps: +- Go to Project -> Project Wizard. +- Click Next +- Select the "PIC16F88" device and click Next +- Select the "Microchip MPASM Toolsuite" and click Next +- Browse to the otgw-4.2.5 directory and enter gateway.mcp as the filename. +- Click Save, then click Next +- Add the files "gateway.asm", "selfprog.asm", "ds1820.asm" and "16f88.lkr" + to the project and click Next +- Click Finish +- Select Project -> Build Configuration -> Release + +The "build.asm" file is supposed to be auto-generated at the start of every +build. The included "build.vbs" script can take care of that. To automatically +invoke the script at the start of each build, do the following: +- Go to Project -> Build options -> Project +- Select the "Custom Build" tab +- Enable the "Pre-Build Step" option +- Specify the command line: WScript "path\to\project\dir\build.vbs" +- Optionally add a project name as an argument on the command line +- Click OK + +Now press F10 to build the project. diff --git a/boiler-monster/original-pic-4.2.5/selfprog.asm b/boiler-monster/original-pic-4.2.5/selfprog.asm new file mode 100644 index 0000000..9e18769 --- /dev/null +++ b/boiler-monster/original-pic-4.2.5/selfprog.asm @@ -0,0 +1,391 @@ + title "Self Programming" + list b=8, r=dec + +;######################################################################## +; Self programming, based on Microchip AN851 +;######################################################################## +; This code has been optimized for size so it would fit in 0x100 code words. +; To achieve this, some special tricks had to be used. As a result the code +; may be a little hard to read +; +; The functionality differs from that described in AN851 in the following +; respects: +; 1. No auto baud detection. Communication must take place at 9600 baud. +; 2. Dropped the first that was used for auto baud detection. +; 3. Dropped the third address byte as it would always be 0. +; 4. The code does not check the last byte of EEPROM data memory to enter boot +; mode. Instead it transmits a character and waits up to 1 second for +; a character. +; 5. The code is designed to be placed in the highest part of memory. That way +; it doesn't interfere with the interrupt code address. It does rely on the +; main program to call the self-program code at an appropriate time. +; 6. Due to the location of the boot loader code, it cannot be protected using +; the code protection bits in the configuration word. This means that the +; boot loader code can also be updated, if necessary. +; 7. The Erase Flash command has been implemented for the PIC16F device. +; 8. The device reset command is 0x08, not any command with a 0 length. +; 9. The version command can be called with a data length of 1-3 words. It will +; report the following information: +; 1. Version +; 2. Boot loader start address +; 3. Boot loader end address +; The software used to reflash the device can use the last two pieces of +; information to determine which part of memory should not be touched. + +#include "p16f88.inc" + + errorlevel -302, -306 + +#define MAJOR_VERSION 1 +#define MINOR_VERSION 1 + +#define STX 0x0F +#define ETX 0x04 +#define DLE 0x05 + +CHKSUM equ 0x70 +COUNTER equ 0x71 +RXDATA equ 0x72 +TXDATA equ 0x73 +TEMP equ 0x74 + +DATA_BUFF equ 0x10 ;Start of receive buffer +COMMAND equ 0x10 ;Data mapped in receive buffer +DATA_COUNT equ 0x11 +ADDRESS_L equ 0x12 +ADDRESS_H equ 0x13 +PACKET_DATA equ 0x14 + +#define SELFRESET PORTB,1 +#define RX PORTB,2 +#define TX PORTB,5 + +#ifdef SELFPROGNEW + #define SELFPROG SelfProgNew + #define STARTADDRESS 0x0700 +#else + #define SELFPROG SelfProg + #define STARTADDRESS 0x0f00 +#endif + + global SELFPROG +SELFPROG code STARTADDRESS + ;Do not go into selfprog mode after a watchdog timeout reset +SELFPROG btfss STATUS,NOT_TO + return ;Let the main code handle a timeout + + bcf INTCON,GIE ;Ignore interrupts + + banksel OSCCON + movlw b'01100000' ;Internal oscillator set to 4MHz + movwf OSCCON ;Configure the oscillator + + ;Set the LEDS as outputs +#ifndef LVP + movlw b'00100111' +#else + movlw b'00101111' +#endif + movwf TRISB + + ;Configure the comparators and voltage reference + ;Allow communication between thermostat and boiler to continue + ;while reprogramming the device + movlw b'11100111' ;Pins 3 and 4 are digital ouputs + movwf TRISA + movlw b'00000111' ;A0 through A2 are used for analog I/O + movwf ANSEL + movlw b'11100110' ;Voltage reference at 1.25V + movwf CVRCON + movlw b'00000110' ;Two common reference comps with output + movwf CMCON + + ;Configure Timer 0 & Watchdog Timer + ;Bit 7 RBPU = 1 - Port B pull up disabled + ;Bit 6 INTEDG = 1 - Interrupt on rising edge + ;Bit 5 T0CS = 0 - Internal clock + ;Bit 4 T0SE = 1 - High to low transition + ;Bit 3 PSA = 0 - Prescaler assigned to Timer 0 + ;Bit 2-0 PS = 7 - 1:256 Prescaler + movlw b'11010111' + movwf OPTION_REG + + movlw 25 ;9600 baud + movwf SPBRG + movlw b'00100110' + movwf TXSTA ;High speed, Asynchronous, Enabled + bcf STATUS,RP0 + bsf RCSTA,SPEN ;Enable serial port + bsf RCSTA,CREN ;Enable receive + + ;Clear the LEDS + movlw b'11111111' + movwf PORTB + + clrf TMR0 + movlw 1 + call Pause +;Notify the external programming software (Pause returns ) + call WrRS232 +;Programmer should react within 1 second + movlw 16 + call Pause + btfss PIR1,RCIF +;No data received, resume the normal program + return +;Check that the received character is + call RdRS232 + skpz +;Other serial data received, resume normal program + return + + bsf STATUS,IRP +ReSync movlw DATA_BUFF + movwf FSR + clrf CHKSUM +GetNextDat call RdRS232 ;Get the data + skpnz + goto ReSync ;Found unprotected STX + xorlw STX ^ ETX ;Check for ETX + skpnz + goto CheckSum ;Yes, examine checksum + xorlw ETX ^ DLE ;Check for DLE + skpnz + call RdRS232 ;Yes, get the next byte + movfw RXDATA + movwf INDF ;Store the data + addwf CHKSUM,F ;Get sum + incf FSR,F + goto GetNextDat + +CheckSum tstf CHKSUM + skpz + goto StartOfLine + bsf STATUS,RP1 + movfw ADDRESS_L + movwf EEADR + movfw ADDRESS_H + movwf EEADRH + movlw PACKET_DATA + movwf FSR + movfw DATA_COUNT + movwf COUNTER +CheckCommand +#ifdef SELFPROGNEW + movlw high ($ + 0x800) +#else + movlw high $ +#endif + movwf PCLATH + movfw COMMAND + sublw 8 + sublw 8 + skpc + goto StartOfLine + addwf PCL,F + goto ReadVersion ;0 Read Version Information + goto ReadProgMem ;1 Read Program Memory + goto WriteProgMem ;2 Write Program Memory + goto EraseProgMem ;3 Erase Program Memory + goto ReadEE ;4 Read EEDATA Memory + goto WriteEE ;5 Write EEDATA Memory + goto StartOfLine ;6 Read Config Memory + goto StartOfLine ;7 Write Config Memory + goto VReset ;8 Reset + +VersionData data (MAJOR_VERSION << 8) | MINOR_VERSION +#ifdef SELFPROGNEW + data SELFPROG + 0x800, SelfProgEnd + 0x800 +#else + data SELFPROG, SelfProgEnd +#endif + +ReadVersion movlw low VersionData + movwf EEADR +#ifdef SELFPROGNEW + movlw high (VersionData + 0x800) +#else + movlw high VersionData +#endif + movwf EEADRH + movlw DATA_BUFF + 2 + movwf FSR +ReadProgMem bsf STATUS,RP0 + bsf EECON1,EEPGD + bsf EECON1,RD + nop + nop + bcf STATUS,RP0 + movfw EEDATA + movwf INDF + incf FSR,F + movfw EEDATH + movwf INDF + incf FSR,F + incf EEADR,F + skpnz + incf EEADRH,F + decfsz COUNTER,F + goto ReadProgMem +WritePacket movlw DATA_BUFF + subwf FSR,W +WritePacketJ1 movwf COUNTER + movlw STX + call WrRS232 + clrf CHKSUM + movlw DATA_BUFF + movwf FSR +SendNext movfw INDF + addwf CHKSUM,F + incf FSR,F + call WrData + decfsz COUNTER,F + goto SendNext + comf CHKSUM,W + addlw 1 + call WrData ;WrData returns + call WrRS232 +StartOfLine bcf STATUS,RP1 + call RdRS232 ;Look for a start of line + skpnz + goto ReSync + goto StartOfLine + +WriteProgMem movlw b'10000100' + call LoadEECon1 + movlw b'11111100' + andwf EEADR,F + movlw 4 + movwf TEMP +Lp1 movfw INDF + movwf EEDATA + incf FSR,F + movfw INDF + movwf EEDATH + incf FSR,F + call StartWrite + decfsz TEMP,F + goto Lp1 + decfsz COUNTER,F + goto WriteProgMem + goto WritePacketJ1 + +EraseProgMem movlw b'10010100' + call LoadEECon1 + movlw 0x1F + iorwf EEADR,F + call StartWrite + decfsz COUNTER,F + goto EraseProgMem + goto WritePacketJ1 + +ReadEE bsf STATUS,RP0 + clrf EECON1 + bsf EECON1,RD + bcf STATUS,RP0 + movfw EEDATA + movwf INDF + incf FSR,F + incf EEADR,F + decfsz COUNTER,F + goto ReadEE + goto WritePacket + +WriteEE movlw b'00000100' + call LoadEECon1 + movfw INDF + movwf EEDATA + incf FSR,F + call StartWrite + decfsz COUNTER,F + goto WriteEE + goto WritePacketJ1 + +WrData movwf TXDATA + xorlw STX + skpnz + goto WrDLE + xorlw STX ^ ETX + skpnz + goto WrDLE + xorlw ETX ^ DLE + skpz + goto WrNext +WrDLE movlw DLE + call WrRS232 +WrNext movfw TXDATA +WrRS232 clrwdt + bcf STATUS,RP1 +WaitTxEmpty btfss PIR1,TXIF + goto WaitTxEmpty + movwf TXREG + retlw ETX + +RdRS232 btfsc RCSTA,OERR + goto VReset +RdLp1 clrwdt + btfss PIR1,RCIF + goto RdLp1 + movfw RCREG + movwf RXDATA + xorlw STX + return + +LoadEECon1 bsf STATUS,RP0 + movwf EECON1 + bcf STATUS,RP0 + return + +StartWrite clrwdt + bsf STATUS,RP0 + movlw 0x55 + movwf EECON2 + movlw 0xAA + movwf EECON2 + bsf EECON1,WR +WaitEEWrite btfsc EECON1,WR ;Skipped when writing program memory + goto WaitEEWrite ;Skipped when writing program memory + bcf STATUS,RP0 + incf EEADR,F + skpnz + incf EEADRH,F + retlw 1 ;Return length of acknowledgement + +Pause clrwdt + btfsc PIR1,RCIF + return + btfss INTCON,TMR0IF + goto Pause + bcf INTCON,TMR0IF + addlw -1 + skpz + goto Pause + retlw ETX + +;Reset the device via an external connection from an I/O pin to the reset pin. +VReset bcf STATUS,RP1 + bcf SELFRESET ;Prepare the output latch + bsf STATUS,RP0 + bcf SELFRESET ;Switch the pin to output + +;If resetting via the output pin doesn't work, restore all used registers to +;their power-on defaults and jump to address 0. +;Do not simply return because the code that called this routine may have been +;wiped and reprogrammed to something completely different. + movlw b'11111111' + movwf TRISA + movwf TRISB + movwf OPTION_REG + movwf ANSEL + movlw b'00000111' + movwf CMCON + clrf TXSTA + clrf SPBRG + clrf STATUS + clrf RCSTA + clrf INTCON + clrf PCLATH + clrwdt +SelfProgEnd goto 0 + + end diff --git a/boiler-monster/pic/.gitignore b/boiler-monster/pic/.gitignore new file mode 100644 index 0000000..f8ab154 --- /dev/null +++ b/boiler-monster/pic/.gitignore @@ -0,0 +1,8 @@ +*.err +*.swp +*.swo +*.lst +*.o +*.hex +*.map +*.cod diff --git a/boiler-monster/pic/16f88.lkr b/boiler-monster/pic/16f88.lkr new file mode 100644 index 0000000..b2b3e95 --- /dev/null +++ b/boiler-monster/pic/16f88.lkr @@ -0,0 +1,76 @@ +// File: 16F88_g.lkr +// Generic linker script for the PIC16F88 processor + +#IFDEF _DEBUG + +LIBPATH . + +CODEPAGE NAME=page0 START=0x0 END=0x7FF +CODEPAGE NAME=page1 START=0x800 END=0xEFF +CODEPAGE NAME=debug START=0xF00 END=0xFFF PROTECTED +CODEPAGE NAME=.idlocs START=0x2000 END=0x2003 PROTECTED +CODEPAGE NAME=.device_id START=0x2006 END=0x2006 PROTECTED +CODEPAGE NAME=.config START=0x2007 END=0x2009 PROTECTED +CODEPAGE NAME=eedata START=0x2100 END=0x21FF PROTECTED + +DATABANK NAME=sfr0 START=0x0 END=0x1F PROTECTED +DATABANK NAME=sfr1 START=0x80 END=0x9F PROTECTED +DATABANK NAME=sfr2 START=0x100 END=0x10F PROTECTED +DATABANK NAME=sfr3 START=0x180 END=0x18F PROTECTED + +SHAREBANK NAME=dbgnobnk START=0x70 END=0x70 PROTECTED +SHAREBANK NAME=dbgnobnk START=0xF0 END=0xF0 PROTECTED +SHAREBANK NAME=dbgnobnk START=0x170 END=0x170 PROTECTED +SHAREBANK NAME=dbgnobnk START=0x1F0 END=0x1F0 PROTECTED + +SHAREBANK NAME=sfrnobnk START=0x71 END=0x7F +SHAREBANK NAME=sfrnobnk START=0xF1 END=0xFF PROTECTED +SHAREBANK NAME=sfrnobnk START=0x171 END=0x17F PROTECTED +SHAREBANK NAME=sfrnobnk START=0x1F1 END=0x1FF PROTECTED + +DATABANK NAME=gpr0 START=0x20 END=0x6F +DATABANK NAME=gpr1 START=0xA0 END=0xEF +DATABANK NAME=gpr2 START=0x110 END=0x16F +DATABANK NAME=gpr3 START=0x190 END=0x1E4 +DATABANK NAME=dbg3 START=0x1E5 END=0x1EF PROTECTED + +SECTION NAME=PROG0 ROM=page0 // ROM code space +SECTION NAME=PROG1 ROM=page1 // ROM code space +SECTION NAME=DEBUG ROM=debug // ICD debug executive +SECTION NAME=IDLOCS ROM=.idlocs // ID locations +SECTION NAME=DEVICEID ROM=.device_id // Device ID +SECTION NAME=DEEPROM ROM=eedata // Data EEPROM + +#ELSE + +LIBPATH . + +CODEPAGE NAME=page0 START=0x0 END=0x7FF +CODEPAGE NAME=page1 START=0x800 END=0xFFF +CODEPAGE NAME=.idlocs START=0x2000 END=0x2003 PROTECTED +CODEPAGE NAME=.device_id START=0x2006 END=0x2006 PROTECTED +CODEPAGE NAME=.config START=0x2007 END=0x2009 PROTECTED +CODEPAGE NAME=eedata START=0x2100 END=0x21FF PROTECTED + +DATABANK NAME=sfr0 START=0x0 END=0x1F PROTECTED +DATABANK NAME=sfr1 START=0x80 END=0x9F PROTECTED +DATABANK NAME=sfr2 START=0x100 END=0x10F PROTECTED +DATABANK NAME=sfr3 START=0x180 END=0x18F PROTECTED + +SHAREBANK NAME=sfrnobnk START=0x70 END=0x7F +SHAREBANK NAME=sfrnobnk START=0xF0 END=0xFF PROTECTED +SHAREBANK NAME=sfrnobnk START=0x170 END=0x17F PROTECTED +SHAREBANK NAME=sfrnobnk START=0x1F0 END=0x1FF PROTECTED + +DATABANK NAME=gpr0 START=0x20 END=0x6F +DATABANK NAME=gpr1 START=0xA0 END=0xEF +DATABANK NAME=gpr2 START=0x110 END=0x16F +DATABANK NAME=gpr3 START=0x190 END=0x1EF + +SECTION NAME=PROG0 ROM=page0 // ROM code space +SECTION NAME=PROG1 ROM=page1 // ROM code space +SECTION NAME=IDLOCS ROM=.idlocs // ID locations +SECTION NAME=DEVICEID ROM=.device_id // Device ID +SECTION NAME=DEEPROM ROM=eedata // Data EEPROM + +#FI diff --git a/boiler-monster/pic/Makefile b/boiler-monster/pic/Makefile new file mode 100644 index 0000000..31ab11c --- /dev/null +++ b/boiler-monster/pic/Makefile @@ -0,0 +1,22 @@ +AFLAGS=-k -p 16F88 -k +LDFLAGS=-m -s 16f88.lkr + +PROG=gateway +ASRCS=${PROG}.asm selfprog.asm +OBJS=${ASRCS:%.asm=%.o} +LSTS=${ASRCS:%.asm=%.err} +ERRS=${ASRCS:%.asm=%.lst} + +default:${PROG}.hex + +%.o:%.asm + gpasm ${AFLAGS} -c -o $@ $< + +${PROG}.hex:${OBJS} + gplink ${LDFLAGS} -o $@ ${OBJS} + + +clean: + /bin/rm -f ${PROG}.hex ${PROG}.map ${PROG}.cod ${OBJS} ${LSTS} ${ERRS} + + diff --git a/boiler-monster/pic/build.asm b/boiler-monster/pic/build.asm new file mode 100644 index 0000000..67d0747 --- /dev/null +++ b/boiler-monster/pic/build.asm @@ -0,0 +1,2 @@ +#define build "1" +#define tstamp "17:59 20-10-2015" diff --git a/boiler-monster/pic/docs/otgw-big-sch.png b/boiler-monster/pic/docs/otgw-big-sch.png new file mode 100644 index 0000000..114e65b Binary files /dev/null and b/boiler-monster/pic/docs/otgw-big-sch.png differ diff --git a/boiler-monster/pic/docs/pinout.txt b/boiler-monster/pic/docs/pinout.txt new file mode 100644 index 0000000..a3db674 --- /dev/null +++ b/boiler-monster/pic/docs/pinout.txt @@ -0,0 +1,33 @@ + +headers: + +left + +GND - STM32 +GPIO_B <- STM32 B9 +GPIO_A <- STM32 B8 +5V -> STM32 +LED_D -> STM32 B7 +LED_C -> STM32 B6 +LED_B -> NC +LED_A -> NC + + +middle + +GND - NC +NC - NC +NC - +PIC RX <- STM32 B10 +PIC TX -> STM32 B11 +*RESET <- STM32 B1 + + +right + +5V -> MR3020 +NC +GND - MR3020 + + + diff --git a/boiler-monster/pic/gateway.asm b/boiler-monster/pic/gateway.asm new file mode 100644 index 0000000..b1c6324 --- /dev/null +++ b/boiler-monster/pic/gateway.asm @@ -0,0 +1,296 @@ + title "OpenTherm Gateway" + list p=16F88, b=8, r=dec + +;Copyright (c) 2009 Schelte Bron + +#define version "4.2" +#define phase "." ;a=alpha, b=beta, .=production +#define patch "5" ;Comment out when not applicable +;#define bugfix "1" ;Comment out when not applicable +#include build.asm + +;See the file "license.txt" for information on usage and redistribution of +;this file, and for a DISCLAIMER OF ALL WARRANTIES. + +#ifndef LVP + __config H'2007', B'10111101110100' +#else + __config H'2007', B'10111111110100' +#endif + __config H'2008', B'11111111111111' + + errorlevel -302 + +;####################################################################### +;Comparator 1 is used for requests. The input is connected to the thermostat. +;In monitor mode, the output is connected to the boiler. +;Comparator 2 is used for responses. The input is connected to the Boiler. +;In monitor mode, the output is connected to the thermostat. + +;The AUSART is configured for full duplex asynchronous serial communication at +;9600 baud, 8 bits, no parity. It is used for reporting about opentherm messages +;and receiving commands. + +;Analog input 0 is used to measure the voltage level on the opentherm line to +;the thermostat. This way the code can distinguish between a real opentherm +;thermostat and a simple on/off thermostat. An opentherm thermostat will keep +;the line at a few volts (low) or between 15 and 18 volts (high). An on/off +;thermostat will either short-circuit the line (0 volts) or leave the line open +;(20+ volts). + +#include "p16f88.inc" + +;Define the speed of the PIC clock +#define mhzstr "4" + +;BAUD contains the SPBRG value for 9600 baud + constant BAUD=25 + + extern SelfProg + +;Variables accessible from all RAM banks + UDATA_SHR +loopcounter res 1 + +Bank0data UDATA 0x20 +rxbuffer res 16 ;Serial receive buffer + +;Variables for longer lasting storage +rxpointer res 1 +txpointer res 1 + +temp res 1 + +Bank1data UDATA 0xA0 + +Bank1values UDATA 0xE0 + +Bank2data UDATA 0x110 +Bank2values UDATA 0x160 + +Bank3data UDATA 0x190 +;Use all available RAM in bank 3 for the transmit buffer + constant TXBUFSIZE=80 +txbuffer res TXBUFSIZE +Bank3values UDATA 0x1E0 + +;I/O map +#define MasterIn CMCON,C1OUT +#define SlaveIn CMCON,C2OUT +#define SlaveOut PORTA,3 +#define MasterOut PORTA,4 +#define RXD PORTB,2 +#define TXD PORTB,5 +#define CCP1 PORTB,0 +#define LED_A PORTB,3 +#define LED_B PORTB,4 +#define LED_C PORTB,6 +#define LED_D PORTB,7 +#define RSET PORTB,1 ;Used by self-programming + +#define SlaveMask b'00001000' +#define MasterMask b'00010000' + +package macro pkg +pkg code +pkg + endm + +pcall macro rtn + lcall rtn + pagesel $ + endm + +;Skip the next instruction (bit 7 of PCLATH is always 0) +skip macro + btfsc PCLATH,7 + endm + +;Get the output of the active comparator into the carry bit +getcompout macro + bsf STATUS,RP0 + rlf CMCON,W ;Get the output of comparator 2 + bcf STATUS,RP0 + btfsc Request ;A request goes through comparator 1 + addlw b'10000000' ;Cause a carry if C1OUT is high + endm + +;The first thing to do upon a reset is to allow the firmware to be updated. +;So no matter how buggy freshly loaded firmware is, if the first two command +;have not been messed up, the device can always be recovered without the need +;for a PIC programmer. The self-programming code times out and returns after +;a second when no firmware update is performed. +; + +ResetVector code 0x0000 + lcall SelfProg ;Always allow a firmware update on boot + lgoto Start ;Start the opentherm gateway program + +InterruptVector code 0x0004 + retfie ;End of interrupt routine + + package Interrupt + +;######################################################################## +; Main program +;######################################################################## + + package Main +Break tstf RCREG ;Clear the RCIF interrupt flag + bcf STATUS,RP1 + bcf STATUS,RP0 ;bank 0 + movlw 128 + call Pause + pcall SelfProg ;Jump to the self-programming code +Start bcf STATUS,RP1 + bcf STATUS,RP0 ;bank 0 + clrf PORTB ;Flash the LED's on startup + bsf STATUS,RP0 + movlw b'01100000' ;Internal oscillator set to 4MHz + movwf OSCCON ;Configure the oscillator + + ;Configure I/O pins + ;Power on defaults all pins to inputs + ;Port A + ;Pins 0 and 1 are used as comparator inputs + ;Pin 2 is used as VREF (must be configured as input!) + ;Pins 3 and 4 are (comparator) ouputs + ;Pin 5 is master reset input + ;Pins 6 and 7 are GPIO + movlw b'11100111' + movwf TRISA + + ;Port B + ;Pins 2 and 5 are used by the USART and don't need to be configured + ;Pins 3, 4, 6, and 7 are used to indicate events for debugging +#ifndef LVP + movlw b'00100111' +#else + ;Can't use RB3 if LVP is enabled + movlw b'00101111' +#endif + movwf TRISB + + movlw b'11' + movwf PCON + + ;Configure Timer 0 & Watchdog Timer + ;Bit 7 RBPU = 1 - Port B pull up disabled + ;Bit 6 INTEDG = 1 - Interrupt on rising edge + ;Bit 5 T0CS = 0 - Internal clock + ;Bit 4 T0SE = 1 - High to low transition + ;Bit 3 PSA = 0 - Prescaler assigned to Timer 0 + ;Bit 2-0 PS = 5 - 1:64 Prescaler + movlw b'11010101' + movwf OPTION_REG + + + ;Configure comparator module + ;Bit 7 C2OUT = R/O + ;Bit 6 C1OUT = R/O + ;Bit 5 C2INV = 0 - Not inverted + ;Bit 4 C1INV = 0 - Not inverted + ;Bit 3 CIS = 0 - Irrelevant + ;Bit 2-0 = 3 - Common reference / 6 - with outputs + movlw b'00000011' ;Common reference mode + movwf CMCON + + movlw b'00000111' ;A0 through A2 are used for analog I/O + movwf ANSEL + + ;Configure the serial interface + movlw BAUD + movwf SPBRG + bsf TXSTA,BRGH ;9600 baud + bcf TXSTA,SYNC ;Asynchronous + bsf TXSTA,TXEN ;Enable transmit + bcf STATUS,RP0 + bsf RCSTA,SPEN ;Enable serial port + bsf RCSTA,CREN ;Enable receive + + ;Configure A/D converter + movlw b'01000001' ;A/D on, channel 0, Fosc/8 + movwf ADCON0 + + ;Configure the voltage reference module + ;The reference voltage must be set to 1.3V + ;Bit 7 VREN = 1 - VREF Enabled + ;Bit 6 VROE = 1 - Output on pin RA2 + ;Bit 5 VRR = 1 - Low range + ;Bit 3-0 VR = 6 - 1.25V (7 - 1.46V) + movlw b'11100110' + bsf STATUS,RP0 + movwf CVRCON ;Set the reference voltage + + + clrf STATUS + bcf PIR2,CMIF ;Clear any comparator interrupt + + movlw 0x41 + movwf TXREG + + + + +MainLoop clrwdt + + ; Copy CMCON bits 7 and 6 from to PORTB bits 7 and 6 (comparitor outputs to LED_C and LED_D pins) + bsf STATUS,RP0 + movfw CMCON + bcf STATUS,RP0 + + andlw b'11000000' + iorwf PORTB,F + iorlw b'00111111' + andwf PORTB,F + + ; Copy PORTA bits 7 and 6 to PORTA bits 4 and 3 (GPIO inputs to transmitter outputs) + rrf PORTA,W + movwf temp + rrf temp,F + rrf temp,W + + andlw b'00011000' + iorwf PORTA,F + iorlw b'11100111' + andwf PORTA,F + + ; And port B bits 4 and 3 (LED_A and LED_B) + + andlw b'00011000' + iorwf PORTB,F + iorlw b'11100111' + andwf PORTB,F + + + btfss PIR1,RCIF ;Activity on the serial receiver? + goto MainLoop + tstf RCREG + + ;FERR is only relevant if RCIF is set + btfsc RCSTA,FERR ;Check for framing error (break) + goto Break + btfss RCSTA,OERR ;Check for overrun error + goto serial_recv + bcf RCSTA,CREN ;Procedure to clear an overrun error + bsf RCSTA,CREN ;Re-enable the serial interface +serial_recv movfw RCREG + ;movwf TXREG + xorlw 0x4 + skpnz + goto Break + goto MainLoop + +Pause clrwdt + btfsc PIR1,RCIF + return + btfss INTCON,TMR0IF + goto Pause + bcf INTCON,TMR0IF + addlw -1 + skpz + goto Pause + return + + + end diff --git a/boiler-monster/pic/selfprog.asm b/boiler-monster/pic/selfprog.asm new file mode 100644 index 0000000..9e18769 --- /dev/null +++ b/boiler-monster/pic/selfprog.asm @@ -0,0 +1,391 @@ + title "Self Programming" + list b=8, r=dec + +;######################################################################## +; Self programming, based on Microchip AN851 +;######################################################################## +; This code has been optimized for size so it would fit in 0x100 code words. +; To achieve this, some special tricks had to be used. As a result the code +; may be a little hard to read +; +; The functionality differs from that described in AN851 in the following +; respects: +; 1. No auto baud detection. Communication must take place at 9600 baud. +; 2. Dropped the first that was used for auto baud detection. +; 3. Dropped the third address byte as it would always be 0. +; 4. The code does not check the last byte of EEPROM data memory to enter boot +; mode. Instead it transmits a character and waits up to 1 second for +; a character. +; 5. The code is designed to be placed in the highest part of memory. That way +; it doesn't interfere with the interrupt code address. It does rely on the +; main program to call the self-program code at an appropriate time. +; 6. Due to the location of the boot loader code, it cannot be protected using +; the code protection bits in the configuration word. This means that the +; boot loader code can also be updated, if necessary. +; 7. The Erase Flash command has been implemented for the PIC16F device. +; 8. The device reset command is 0x08, not any command with a 0 length. +; 9. The version command can be called with a data length of 1-3 words. It will +; report the following information: +; 1. Version +; 2. Boot loader start address +; 3. Boot loader end address +; The software used to reflash the device can use the last two pieces of +; information to determine which part of memory should not be touched. + +#include "p16f88.inc" + + errorlevel -302, -306 + +#define MAJOR_VERSION 1 +#define MINOR_VERSION 1 + +#define STX 0x0F +#define ETX 0x04 +#define DLE 0x05 + +CHKSUM equ 0x70 +COUNTER equ 0x71 +RXDATA equ 0x72 +TXDATA equ 0x73 +TEMP equ 0x74 + +DATA_BUFF equ 0x10 ;Start of receive buffer +COMMAND equ 0x10 ;Data mapped in receive buffer +DATA_COUNT equ 0x11 +ADDRESS_L equ 0x12 +ADDRESS_H equ 0x13 +PACKET_DATA equ 0x14 + +#define SELFRESET PORTB,1 +#define RX PORTB,2 +#define TX PORTB,5 + +#ifdef SELFPROGNEW + #define SELFPROG SelfProgNew + #define STARTADDRESS 0x0700 +#else + #define SELFPROG SelfProg + #define STARTADDRESS 0x0f00 +#endif + + global SELFPROG +SELFPROG code STARTADDRESS + ;Do not go into selfprog mode after a watchdog timeout reset +SELFPROG btfss STATUS,NOT_TO + return ;Let the main code handle a timeout + + bcf INTCON,GIE ;Ignore interrupts + + banksel OSCCON + movlw b'01100000' ;Internal oscillator set to 4MHz + movwf OSCCON ;Configure the oscillator + + ;Set the LEDS as outputs +#ifndef LVP + movlw b'00100111' +#else + movlw b'00101111' +#endif + movwf TRISB + + ;Configure the comparators and voltage reference + ;Allow communication between thermostat and boiler to continue + ;while reprogramming the device + movlw b'11100111' ;Pins 3 and 4 are digital ouputs + movwf TRISA + movlw b'00000111' ;A0 through A2 are used for analog I/O + movwf ANSEL + movlw b'11100110' ;Voltage reference at 1.25V + movwf CVRCON + movlw b'00000110' ;Two common reference comps with output + movwf CMCON + + ;Configure Timer 0 & Watchdog Timer + ;Bit 7 RBPU = 1 - Port B pull up disabled + ;Bit 6 INTEDG = 1 - Interrupt on rising edge + ;Bit 5 T0CS = 0 - Internal clock + ;Bit 4 T0SE = 1 - High to low transition + ;Bit 3 PSA = 0 - Prescaler assigned to Timer 0 + ;Bit 2-0 PS = 7 - 1:256 Prescaler + movlw b'11010111' + movwf OPTION_REG + + movlw 25 ;9600 baud + movwf SPBRG + movlw b'00100110' + movwf TXSTA ;High speed, Asynchronous, Enabled + bcf STATUS,RP0 + bsf RCSTA,SPEN ;Enable serial port + bsf RCSTA,CREN ;Enable receive + + ;Clear the LEDS + movlw b'11111111' + movwf PORTB + + clrf TMR0 + movlw 1 + call Pause +;Notify the external programming software (Pause returns ) + call WrRS232 +;Programmer should react within 1 second + movlw 16 + call Pause + btfss PIR1,RCIF +;No data received, resume the normal program + return +;Check that the received character is + call RdRS232 + skpz +;Other serial data received, resume normal program + return + + bsf STATUS,IRP +ReSync movlw DATA_BUFF + movwf FSR + clrf CHKSUM +GetNextDat call RdRS232 ;Get the data + skpnz + goto ReSync ;Found unprotected STX + xorlw STX ^ ETX ;Check for ETX + skpnz + goto CheckSum ;Yes, examine checksum + xorlw ETX ^ DLE ;Check for DLE + skpnz + call RdRS232 ;Yes, get the next byte + movfw RXDATA + movwf INDF ;Store the data + addwf CHKSUM,F ;Get sum + incf FSR,F + goto GetNextDat + +CheckSum tstf CHKSUM + skpz + goto StartOfLine + bsf STATUS,RP1 + movfw ADDRESS_L + movwf EEADR + movfw ADDRESS_H + movwf EEADRH + movlw PACKET_DATA + movwf FSR + movfw DATA_COUNT + movwf COUNTER +CheckCommand +#ifdef SELFPROGNEW + movlw high ($ + 0x800) +#else + movlw high $ +#endif + movwf PCLATH + movfw COMMAND + sublw 8 + sublw 8 + skpc + goto StartOfLine + addwf PCL,F + goto ReadVersion ;0 Read Version Information + goto ReadProgMem ;1 Read Program Memory + goto WriteProgMem ;2 Write Program Memory + goto EraseProgMem ;3 Erase Program Memory + goto ReadEE ;4 Read EEDATA Memory + goto WriteEE ;5 Write EEDATA Memory + goto StartOfLine ;6 Read Config Memory + goto StartOfLine ;7 Write Config Memory + goto VReset ;8 Reset + +VersionData data (MAJOR_VERSION << 8) | MINOR_VERSION +#ifdef SELFPROGNEW + data SELFPROG + 0x800, SelfProgEnd + 0x800 +#else + data SELFPROG, SelfProgEnd +#endif + +ReadVersion movlw low VersionData + movwf EEADR +#ifdef SELFPROGNEW + movlw high (VersionData + 0x800) +#else + movlw high VersionData +#endif + movwf EEADRH + movlw DATA_BUFF + 2 + movwf FSR +ReadProgMem bsf STATUS,RP0 + bsf EECON1,EEPGD + bsf EECON1,RD + nop + nop + bcf STATUS,RP0 + movfw EEDATA + movwf INDF + incf FSR,F + movfw EEDATH + movwf INDF + incf FSR,F + incf EEADR,F + skpnz + incf EEADRH,F + decfsz COUNTER,F + goto ReadProgMem +WritePacket movlw DATA_BUFF + subwf FSR,W +WritePacketJ1 movwf COUNTER + movlw STX + call WrRS232 + clrf CHKSUM + movlw DATA_BUFF + movwf FSR +SendNext movfw INDF + addwf CHKSUM,F + incf FSR,F + call WrData + decfsz COUNTER,F + goto SendNext + comf CHKSUM,W + addlw 1 + call WrData ;WrData returns + call WrRS232 +StartOfLine bcf STATUS,RP1 + call RdRS232 ;Look for a start of line + skpnz + goto ReSync + goto StartOfLine + +WriteProgMem movlw b'10000100' + call LoadEECon1 + movlw b'11111100' + andwf EEADR,F + movlw 4 + movwf TEMP +Lp1 movfw INDF + movwf EEDATA + incf FSR,F + movfw INDF + movwf EEDATH + incf FSR,F + call StartWrite + decfsz TEMP,F + goto Lp1 + decfsz COUNTER,F + goto WriteProgMem + goto WritePacketJ1 + +EraseProgMem movlw b'10010100' + call LoadEECon1 + movlw 0x1F + iorwf EEADR,F + call StartWrite + decfsz COUNTER,F + goto EraseProgMem + goto WritePacketJ1 + +ReadEE bsf STATUS,RP0 + clrf EECON1 + bsf EECON1,RD + bcf STATUS,RP0 + movfw EEDATA + movwf INDF + incf FSR,F + incf EEADR,F + decfsz COUNTER,F + goto ReadEE + goto WritePacket + +WriteEE movlw b'00000100' + call LoadEECon1 + movfw INDF + movwf EEDATA + incf FSR,F + call StartWrite + decfsz COUNTER,F + goto WriteEE + goto WritePacketJ1 + +WrData movwf TXDATA + xorlw STX + skpnz + goto WrDLE + xorlw STX ^ ETX + skpnz + goto WrDLE + xorlw ETX ^ DLE + skpz + goto WrNext +WrDLE movlw DLE + call WrRS232 +WrNext movfw TXDATA +WrRS232 clrwdt + bcf STATUS,RP1 +WaitTxEmpty btfss PIR1,TXIF + goto WaitTxEmpty + movwf TXREG + retlw ETX + +RdRS232 btfsc RCSTA,OERR + goto VReset +RdLp1 clrwdt + btfss PIR1,RCIF + goto RdLp1 + movfw RCREG + movwf RXDATA + xorlw STX + return + +LoadEECon1 bsf STATUS,RP0 + movwf EECON1 + bcf STATUS,RP0 + return + +StartWrite clrwdt + bsf STATUS,RP0 + movlw 0x55 + movwf EECON2 + movlw 0xAA + movwf EECON2 + bsf EECON1,WR +WaitEEWrite btfsc EECON1,WR ;Skipped when writing program memory + goto WaitEEWrite ;Skipped when writing program memory + bcf STATUS,RP0 + incf EEADR,F + skpnz + incf EEADRH,F + retlw 1 ;Return length of acknowledgement + +Pause clrwdt + btfsc PIR1,RCIF + return + btfss INTCON,TMR0IF + goto Pause + bcf INTCON,TMR0IF + addlw -1 + skpz + goto Pause + retlw ETX + +;Reset the device via an external connection from an I/O pin to the reset pin. +VReset bcf STATUS,RP1 + bcf SELFRESET ;Prepare the output latch + bsf STATUS,RP0 + bcf SELFRESET ;Switch the pin to output + +;If resetting via the output pin doesn't work, restore all used registers to +;their power-on defaults and jump to address 0. +;Do not simply return because the code that called this routine may have been +;wiped and reprogrammed to something completely different. + movlw b'11111111' + movwf TRISA + movwf TRISB + movwf OPTION_REG + movwf ANSEL + movlw b'00000111' + movwf CMCON + clrf TXSTA + clrf SPBRG + clrf STATUS + clrf RCSTA + clrf INTCON + clrf PCLATH + clrwdt +SelfProgEnd goto 0 + + end diff --git a/boiler-monster/stm32/.gitignore b/boiler-monster/stm32/.gitignore new file mode 100644 index 0000000..64532f4 --- /dev/null +++ b/boiler-monster/stm32/.gitignore @@ -0,0 +1,15 @@ +*.o +*.d +*.map +*.hex +*.elf +*.swp +*~ +*.dfu +plot/data +plot/script +*.bin +scmversion +commit.h +opencm3.build.stamp +*.orig diff --git a/boiler-monster/stm32/Makefile b/boiler-monster/stm32/Makefile new file mode 100644 index 0000000..e1cd497 --- /dev/null +++ b/boiler-monster/stm32/Makefile @@ -0,0 +1,8 @@ + +default: + make -C libopencm3 + make -C app + +clean: + make -C libopencm3 clean + make -C app clean diff --git a/boiler-monster/stm32/Makefile.include b/boiler-monster/stm32/Makefile.include new file mode 100644 index 0000000..4f5cbd9 --- /dev/null +++ b/boiler-monster/stm32/Makefile.include @@ -0,0 +1,44 @@ +## +## This file is part of the libopencm3 project. +## +## Copyright (C) 2009 Uwe Hermann +## Copyright (C) 2010 Piotr Esden-Tempski +## +## This library is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU Lesser General Public License for more details. +## +## You should have received a copy of the GNU Lesser General Public License +## along with this library. If not, see . +## + +LIBNAME = opencm3_stm32f1 +DEFS = -DSTM32F1 + +FP_FLAGS ?= -msoft-float +ARCH_FLAGS = -mthumb -mcpu=cortex-m3 $(FP_FLAGS) -mfix-cortex-m3-ldrd + +################################################################################ +# OpenOCD specific variables + +OOCD ?= openocd +OOCD_INTERFACE ?= interface/stlink-v2.cfg +OOCD_BOARD ?= target/stm32f1x.cfg + +################################################################################ +# Black Magic Probe specific variables +# Set the BMP_PORT to a serial port and then BMP is used for flashing +BMP_PORT ?= + +################################################################################ +# texane/stlink specific variables +#STLINK_PORT ?= :4242 + + +include ../Makefile.rules diff --git a/boiler-monster/stm32/Makefile.rules b/boiler-monster/stm32/Makefile.rules new file mode 100644 index 0000000..a723e6f --- /dev/null +++ b/boiler-monster/stm32/Makefile.rules @@ -0,0 +1,261 @@ +# +## This file is part of the libopencm3 project. +## +## Copyright (C) 2009 Uwe Hermann +## Copyright (C) 2010 Piotr Esden-Tempski +## Copyright (C) 2013 Frantisek Burian +## +## This library is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU Lesser General Public License for more details. +## +## You should have received a copy of the GNU Lesser General Public License +## along with this library. If not, see . +## + +# Be silent per default, but 'make V=1' will show all compiler calls. +ifneq ($(V),1) +Q := @ +NULL := 2>/dev/null +endif + +############################################################################### +# Executables + +PREFIX ?= arm-none-eabi + +CC := $(PREFIX)-gcc +CXX := $(PREFIX)-g++ +LD := $(PREFIX)-gcc +AR := $(PREFIX)-ar +AS := $(PREFIX)-as +OBJCOPY := $(PREFIX)-objcopy +OBJDUMP := $(PREFIX)-objdump +GDB := $(PREFIX)-gdb +STFLASH = $(shell which st-flash) +STYLECHECK := /checkpatch.pl +STYLECHECKFLAGS := --no-tree -f --terse --mailback +STYLECHECKFILES := $(shell find . -name '*.[ch]') + + +############################################################################### +# Source files + +LDSCRIPT ?= $(BINARY).ld + +#OBJS += $(BINARY).o + + +ifeq ($(strip $(OPENCM3_DIR)),) +# user has not specified the library path, so we try to detect it + +# where we search for the library +LIBPATHS := ./libopencm3 ../libopencm3 + +OPENCM3_DIR := $(wildcard $(LIBPATHS:=/locm3.sublime-project)) +OPENCM3_DIR := $(firstword $(dir $(OPENCM3_DIR))) + +ifeq ($(strip $(OPENCM3_DIR)),) +$(warning Cannot find libopencm3 library in the standard search paths.) +$(error Please specify it through OPENCM3_DIR variable!) +endif +endif + +ifeq ($(V),1) +$(info Using $(OPENCM3_DIR) path to library) +endif + +INCLUDE_DIR = $(OPENCM3_DIR)/include +LIB_DIR = $(OPENCM3_DIR)/lib +SCRIPT_DIR = $(OPENCM3_DIR)/scripts + +############################################################################### +# C flags + +CFLAGS += -Os -g +CFLAGS += -Wextra -Wimplicit-function-declaration +CFLAGS += -Wmissing-prototypes -Wstrict-prototypes +CFLAGS += -fno-common -ffunction-sections -fdata-sections + +############################################################################### +# C++ flags + +CXXFLAGS += -Os -g +CXXFLAGS += -Wextra -Wshadow -Wredundant-decls -Weffc++ +CXXFLAGS += -fno-common -ffunction-sections -fdata-sections + +############################################################################### +# C & C++ preprocessor common flags + +CPPFLAGS += -MD +CPPFLAGS += -Wall -Wundef + +INCLUDES = -I$(INCLUDE_DIR) +DEFINES = $(DEFS) + +CPPFLAGS += $(INCLUDES) $(DEFINES) + +############################################################################### +# Linker flags + +LDFLAGS += --static -nostartfiles +LDFLAGS += -L$(LIB_DIR) +LDFLAGS += -T$(LDSCRIPT) +LDFLAGS += -Wl,-Map=$(*).map +LDFLAGS += -Wl,--gc-sections +ifeq ($(V),99) +LDFLAGS += -Wl,--print-gc-sections +endif + +############################################################################### +# Used libraries + +LDLIBS += -l$(LIBNAME) +LDLIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group + +############################################################################### +############################################################################### +############################################################################### + +.SUFFIXES: .elf .bin .hex .srec .list .map .images .dfu +.SECONDEXPANSION: +.SECONDARY: + +all: elf + + +elf: $(BINARY).elf +bin: $(BINARY).bin +hex: $(BINARY).hex +srec: $(BINARY).srec +list: $(BINARY).list + +images: $(BINARY).images +flash: $(BINARY).flash + +%.images: %.bin %.hex %.srec %.list %.map %.dfu + @#printf "*** $* images generated ***\n" + +%.bin: %.elf + @#printf " OBJCOPY $(*).bin\n" + $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).bin + +%.hex: %.elf + @#printf " OBJCOPY $(*).hex\n" + $(Q)$(OBJCOPY) -Oihex $(*).elf $(*).hex + +%.dfu: %.elf + @#printf " OBJCOPY $(*).dfu\n" + $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).dfu + +%.srec: %.elf + @#printf " OBJCOPY $(*).srec\n" + $(Q)$(OBJCOPY) -Osrec $(*).elf $(*).srec + +%.list: %.elf + @#printf " OBJDUMP $(*).list\n" + $(Q)$(OBJDUMP) -S $(*).elf > $(*).list + +fish: + echo %.elf %.map: $(OBJS) $(LDSCRIPT) $(LIB_DIR)/lib$(LIBNAME).a + echo $(BINARY).elf + +%.elf %.map: $(OBJS) $(LDSCRIPT) $(LIB_DIR)/lib$(LIBNAME).a + @#printf " LD $(*).elf\n" + $(Q)$(LD) $(LDFLAGS) $(ARCH_FLAGS) $(OBJS) $(LDLIBS) -o $(*).elf + +%.o: %.c $(OPENCM3_DIR)/../opencm3.build.stamp + @#printf " CC $(*).c\n" + $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $(*).o -c $(*).c + +%.o: %.cxx $(OPENCM3_DIR)/../opencm3.build.stamp + @#printf " CXX $(*).cxx\n" + $(Q)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $(*).o -c $(*).cxx + +%.o: %.cpp $(OPENCM3_DIR)/../opencm3.build.stamp + @#printf " CXX $(*).cpp\n" + $(Q)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $(*).o -c $(*).cpp + +clean: + @#printf " CLEAN\n" + $(Q)$(RM) *.o *.d *.elf *.bin *.hex *.srec *.list *.map *~ *.dfu ${EXTRACLEANS} + +stylecheck: $(STYLECHECKFILES:=.stylecheck) +styleclean: $(STYLECHECKFILES:=.styleclean) + +# the cat is due to multithreaded nature - we like to have consistent chunks of text on the output +%.stylecheck: % + $(Q)$(SCRIPT_DIR)$(STYLECHECK) $(STYLECHECKFLAGS) $* > $*.stylecheck; \ + if [ -s $*.stylecheck ]; then \ + cat $*.stylecheck; \ + else \ + rm -f $*.stylecheck; \ + fi; + +%.styleclean: + $(Q)rm -f $*.stylecheck; + + +%.stlink-flash: %.bin + @printf " FLASH $<\n" + $(Q)$(STFLASH) write $(*).bin 0x8000000 + +ifeq ($(STLINK_PORT),) +ifeq ($(BMP_PORT),) +ifeq ($(OOCD_SERIAL),) +%.flash: %.hex + @printf " FLASH $<\n" + @# IMPORTANT: Don't use "resume", only "reset" will work correctly! + $(Q)$(OOCD) -f $(OOCD_INTERFACE) \ + -f $(OOCD_BOARD) \ + -c "init" -c "reset init" \ + -c "flash write_image erase $(*).hex" \ + -c "reset" \ + -c "shutdown" $(NULL) +else +%.flash: %.hex + @printf " FLASH $<\n" + @# IMPORTANT: Don't use "resume", only "reset" will work correctly! + $(Q)$(OOCD) -f $(OOCD_INTERFACE) \ + -f $(OOCD_BOARD) \ + -c "ft2232_serial $(OOCD_SERIAL)" \ + -c "init" -c "reset init" \ + -c "flash write_image erase $(*).hex" \ + -c "reset" \ + -c "shutdown" $(NULL) +endif +else +%.flash: %.elf + @printf " GDB $(*).elf (flash)\n" + $(Q)$(GDB) --batch \ + -ex 'target extended-remote $(BMP_PORT)' \ + -x $(SCRIPT_DIR)/black_magic_probe_flash.scr \ + $(*).elf +endif +else +%.flash: %.elf + @printf " GDB $(*).elf (flash)\n" + $(Q)$(GDB) --batch \ + -ex 'target extended-remote $(STLINK_PORT)' \ + -x $(SCRIPT_DIR)/stlink_flash.scr \ + $(*).elf +endif + +.PHONY: images clean stylecheck styleclean elf bin hex srec list + +-include $(OBJS:.o=.d) + + +$(LIB_DIR)/lib$(LIBNAME).a: $(OPENCM3_DIR)/../opencm3.build.stamp + + +$(OPENCM3_DIR)/../opencm3.build.stamp: + ${MAKE} -C ${OPENCM3_DIR} + touch $@ + diff --git a/boiler-monster/stm32/app/1wire.c b/boiler-monster/stm32/app/1wire.c new file mode 100644 index 0000000..c374c5c --- /dev/null +++ b/boiler-monster/stm32/app/1wire.c @@ -0,0 +1,392 @@ +#include "project.h" + +#define USART2_TX GPIO_USART2_TX +#define USART2_TX_PORT GPIOA + +#define BIT_ZERO '0' +#define BIT_ONE '1' +#define BIT_UNKNOWN 'U' +#define BIT_CONFLICT 'C' + + +static int poke; +static volatile unsigned exchange_timeout; + +void onewire_tick (void) +{ + static unsigned ticker; + + if (exchange_timeout) + exchange_timeout--; + + + ticker++; + + if (ticker < MS_TO_TICKS (5000)) + return; + + ticker = 0; + poke = 1; + + +} + + +static unsigned usart_send_ready (uint32_t usart) +{ + return (USART_SR (usart) & USART_SR_TXE); +} + + +static unsigned usart_recv_ready (uint32_t usart) +{ + return (USART_SR (usart) & USART_SR_RXNE); +} + + + +static int onewire_exchange (int tx, unsigned timeout) +{ + int rx; + (void) USART_SR (USART2); + (void) usart_recv (USART2); + + + exchange_timeout = MS_TO_TICKS (timeout); + + while (!usart_send_ready (USART2)) + if (!exchange_timeout) + return -1; + + usart_send (USART2, tx); + + exchange_timeout = MS_TO_TICKS (timeout); + + while (!usart_recv_ready (USART2)) + if (!exchange_timeout) + return -1; + + + rx = usart_recv (USART2); + + return rx; + +} + +/* returns 1 if nothing on bus or error */ +int +onewire_reset (void) +{ + int ret; + + usart_set_baudrate (USART2, 9600); + delay_ms (1); + ret = onewire_exchange (0xf0, 60); + usart_set_baudrate (USART2, 115200); + delay_ms (1); + + return (ret == 0xf0); +} + +static void +onewire_one (void) +{ + onewire_exchange (0xff, 3); +} + +static void +onewire_zero (void) +{ + onewire_exchange (0x00, 3); +} + +static int +onewire_read (void) +{ + uint8_t rx; + + rx = onewire_exchange (0xff, 3); + + return (rx == 0xff); +} + + +void +onewire_write_byte (uint8_t v) +{ + int c; + + + for (c = 0; c < 8; ++c) { + if (v & 1) + onewire_one(); + else + onewire_zero(); + + v >>= 1; + } +} + +uint8_t +onewire_read_byte (void) +{ + uint8_t v = 0; + int c; + + + for (c = 0; c < 8; ++c) { + v >>= 1; + + if (onewire_read()) + v |= 0x80; + } + + + return v; +} + + + +void +onewire_read_bytes (uint8_t *buf, int n) +{ + while (n--) + * (buf++) = onewire_read_byte(); +} + + +void +onewire_write_bytes (const uint8_t *buf, int n) +{ + while (n--) + onewire_write_byte (* (buf++)); +} + + +int onewire_select (const Onewire_addr *a) +{ + if (!a) + onewire_write_byte (ONEWIRE_SKIP_ROM); + else { + onewire_write_byte (ONEWIRE_MATCH_ROM); + onewire_write_bytes (a->a, 8); + } + + return 0; +} + + +int onewire_reset_and_select (const Onewire_addr *a) +{ + if (onewire_reset()) + return 1; + + if (onewire_select (a)) + return 1; + + return 0; +} + + + +int onewire_wait_complete (unsigned timeout) +{ + while (! (onewire_read())) { + delay_ms (10); + + if (! (timeout--)) + return 1; + } + + return 0; +} + + + +int +onewire_check_crc (uint8_t *buf, int n, uint8_t v) +{ + uint8_t crc = 0; + int i; + + while (n--) { + uint8_t v = * (buf++); + + for (i = 0; i < 8; ++i) { + uint8_t mix = (crc ^ v) & 0x01; + crc >>= 1; + + if (mix) + crc ^= 0x8C; + + v >>= 1; + } + } + + return ! (crc == v); + +} + +static int onewire_conduct_search (uint8_t *bits) +{ + unsigned i, ir, r; + + if (onewire_reset()) + return -1; + + onewire_write_byte (ONEWIRE_SEARCH_ROM); + + + for (i = 0; i < 64; ++i) { + + r = onewire_read(); + ir = onewire_read(); + +#if 0 + + if ((i == 27) || (i == 24) || (i == 39)) + ir = r = 0; + +#endif + + switch (bits[i]) { + case BIT_UNKNOWN: + + if (!r && ir) { /* Zero */ + bits[i] = BIT_ZERO; + onewire_zero(); + } else if (r && !ir) { /*One */ + bits[i] = BIT_ONE; + onewire_one(); + } else if (r && ir) { /*Nothing here */ + //MEH; + return -1; + } else if (!r && !ir) { /*Both */ + bits[i] = BIT_CONFLICT; + onewire_zero(); + } + + break; + + case BIT_CONFLICT: + if (!r && !ir) /*Both */ + onewire_zero(); + else { + //MEH; + return -1; + } + + break; + + case BIT_ZERO: + if (!r) + onewire_zero(); + else { + //MEH; + return -1; + } + + break; + + case BIT_ONE: + if (!ir) + onewire_one(); + else { + //MEH; + return -1; + } + + break; + } + } + + return 0; +} + +static int onewire_next (uint8_t *bits) +{ + unsigned i; + + for (i = 0; i < 64; ++i) { + + if (bits[63 - i] == BIT_CONFLICT) { + bits[63 - i] = BIT_ONE; + return 1; + } + + bits[63 - i] = BIT_UNKNOWN; + } + + return 0; +} + +static void onewire_bits_to_a (uint8_t *bits, Onewire_addr *a) +{ + unsigned i, j, c; + + for (i = 0, j = 0; i < 8; ++i) { + + a->a[i] = 0; + + for (c = 1; c < 0x100; c <<= 1, ++j) { + if (bits[j] == BIT_ONE) + a->a[i] |= c; + } + } +} + +int onewire_search (void) +{ + uint8_t bits[64]; + Onewire_addr a; + int r, c; + + delay_ms (2000); + + memset (bits, BIT_UNKNOWN, sizeof (bits)); + + do { + r = onewire_conduct_search (bits); + + if (!r) { + onewire_bits_to_a (bits, &a); + c = onewire_check_crc (&a.a[0], 7, a.a[7]); + + if (!c) { + printf ("QO: {0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x}\r\n", + a.a[0], + a.a[1], + a.a[2], + a.a[3], + a.a[4], + a.a[5], + a.a[6], + a.a[7]); + } + + } + + } while (onewire_next (bits)); + + + return 0; +} + + + +void +onewire_init (void) +{ + MAP_AF_OD (USART2_TX); + + usart_set_baudrate (USART2, 115200); + usart_set_databits (USART2, 8); + usart_set_stopbits (USART2, USART_STOPBITS_1); + usart_set_parity (USART2, USART_PARITY_NONE); + usart_set_flow_control (USART2, USART_FLOWCONTROL_NONE); + usart_set_mode (USART2, USART_MODE_TX_RX); + + USART_CR3 (USART2) |= USART_CR3_HDSEL; + + usart_enable (USART2); +} diff --git a/boiler-monster/stm32/app/1wire.h b/boiler-monster/stm32/app/1wire.h new file mode 100644 index 0000000..4a31418 --- /dev/null +++ b/boiler-monster/stm32/app/1wire.h @@ -0,0 +1,10 @@ +#define ONEWIRE_SKIP_ROM 0xcc +#define ONEWIRE_SEARCH_ROM 0xf0 +#define ONEWIRE_MATCH_ROM 0x55 + +#define ONEWIRE_ADDR_LEN 8 +typedef struct { + uint8_t a[ONEWIRE_ADDR_LEN]; +} Onewire_addr; + + diff --git a/boiler-monster/stm32/app/Makefile b/boiler-monster/stm32/app/Makefile new file mode 100644 index 0000000..23b4a74 --- /dev/null +++ b/boiler-monster/stm32/app/Makefile @@ -0,0 +1,113 @@ +## +## This file is part of the libopencm3 project. +## +## Copyright (C) 2009 Uwe Hermann +## +## This library is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU Lesser General Public License for more details. +## +## You should have received a copy of the GNU Lesser General Public License +## along with this library. If not, see . +## + +CPROTO=cproto +PROG=boiler + +CSRCS=main.c ot.c ot_phy_rx.c ot_phy_tx.c led.c ticker.c usart.c ring.c stdio.c util.c commit.c cmd.c pic.c 1wire.c temp.c ds1820.c pressure.c adc.c +HSRCS=pins.h project.h ring.h + +EXTRACLEANS=*.orig scmversion commit.h + +TARGET=boiler-monster + +V=1 +default: ${PROG}.fw.bin + rsync -vaP ${PROG}.fw.bin ${TARGET}:/tmp/boiler.fw + ssh ${TARGET} /etc/stm32/startup /tmp/boiler.fw + + +install: ${PROG}.fw.bin + rsync -vaP ${PROG}.fw.bin ${TARGET}:/etc/stm32/boiler.fw + + +128k_of_ff.bin: + dd if=/dev/zero bs=128k count=1 | tr '\0' '\377' > $@ + + +${PROG}.fw.bin:${PROG}.bin 128k_of_ff.bin + cat ${PROG}.bin 128k_of_ff.bin | dd of=$@ bs=128k iflag=fullblock count=1 + + +program: ${PROG}.elf + echo halt | nc -t localhost 4444 + echo flash write_image erase ${PWD}/$< 0x2000 | nc -t localhost 4444 + echo reset halt | nc -t localhost 4444 + RV=$$(arm-none-eabi-objdump --disassemble=reset_handler $< | awk '/^0/ { print "0x"$$1 }') && \ + echo resume $${RV} | nc -t localhost 4444 && \ + echo reset vector at $${RV} + +run:${PROG}.run + +%.run: %.hex + RV=$$(arm-none-eabi-objdump --disassemble=reset_handler $(*).elf | awk '/^0/ { print "0x"$$1 }') && \ + $(Q)$(OOCD) -f $(OOCD_INTERFACE) \ + -f $(OOCD_BOARD) \ + -c "init" -c "reset init" \ + -c "flash write_image erase $(*).hex" \ + -c "reset halt" \ + -c "resume $${RV}" \ + -c "shutdown" $(NULL) && \ + echo reset_handler is at $${RV} + + + + +BINARY = ${PROG} +OBJS = ${CSRCS:%.c=%.o} + +include ../Makefile.include + +CFLAGS+=-Wno-redundant-decls -Wno-unused-parameter -DDEBUG + +INCLUDES += -I.. + +protos: + echo -n > prototypes.h + ${CPROTO} $(INCLUDES) $(DEFINES) -e -v ${CSRCS} > prototypes.h.tmp + mv -f prototypes.h.tmp prototypes.h + +server: + $(OOCD) -f $(OOCD_INTERFACE) \ + -f $(OOCD_BOARD) + +debug: ${PROG}.elf + ${PREFIX}-gdb -x gdb.script ${PROG}.elf + +tidy: + astyle -A3 -s2 --attach-extern-c -L -c -w -Y -m0 -f -p -H -U -k3 -xj -xd ${CSRCS} ${HSRCS} + +HEAD_REF = $(shell git rev-parse --verify --short HEAD) +SCM_DIRTY = $(shell if ! git diff-index --quiet HEAD --; then echo "+dirty"; fi) +SCMVERSION = ${HEAD_REF}${SCM_DIRTY} + +scmversion: + echo -n ${SCMVERSION} > $@ + +.PHONY: scmversion + +commit.o: commit.h + +commit.h: scmversion + echo -n '#define COMMIT_ID "' > $@ + echo -n `cat scmversion` >> $@ + echo '"' >> $@ + + + diff --git a/boiler-monster/stm32/app/adc.c b/boiler-monster/stm32/app/adc.c new file mode 100644 index 0000000..2cfc4e3 --- /dev/null +++ b/boiler-monster/stm32/app/adc.c @@ -0,0 +1,154 @@ +#include "project.h" + +#define T do { printf("%s:%d\r\n",__FILE__,__LINE__); } while (0) + +void adc_dump (void) +{ + printf ("ADC_SR %x ADC_CR1 %x ADC_CR2 %x\r\n", (unsigned) ADC_SR (ADC1), + (unsigned) ADC_CR1 (ADC1), + (unsigned) ADC_CR2 (ADC1)); + +} + + +volatile unsigned timeout; + + +void adc_tick (void) +{ + if (timeout) timeout--; +} + + +static int adc_wait (volatile uint32_t *reg, uint32_t wait_while_set, uint32_t wait_while_clear, unsigned ms) +{ + timeout = MS_TO_TICKS (ms); + + + while ((*reg) & wait_while_set) + if (!timeout) { + printf ("QADC timeout\r\n"); + return -1; + } + + + while ((~ (*reg)) & wait_while_clear) + if (!timeout) { + printf ("QADC timeout\r\n"); + return -1; + } + + + return 0; +} + + + +int adc_calibrate (void) +{ + adc_off (ADC1); + adc_power_on (ADC1); + delay_ms (5); + + ADC_CR2 (ADC1) |= ADC_CR2_RSTCAL; + + if (adc_wait (&ADC_CR2 (ADC1), ADC_CR2_CAL, 0, 2)) return -1; + + ADC_CR2 (ADC1) |= ADC_CR2_CAL; + + if (adc_wait (&ADC_CR2 (ADC1), ADC_CR2_CAL, 0, 2)) return -1; + + return 0; +} + + + +int adc_convert_start (unsigned channel) +{ + uint8_t ch = channel; + + adc_set_regular_sequence (ADC1, 1, &ch); + + /* Start conversion on regular channels. */ + ADC_CR2 (ADC1) |= ADC_CR2_SWSTART; + +#if 0 + if (adc_wait (&ADC_CR2 (ADC1), ADC_CR2_SWSTART, 0, 2)) + return -1; +#endif + + return 0; +} + + + +int adc_convert_done(void) +{ + + if (ADC_SR(ADC1) & ADC_SR_EOC) + return 1; + + return 0; +} + +unsigned adc_convert_get(void) +{ + return adc_read_regular (ADC1); +} + + +unsigned adc_convert (unsigned channel) +{ + uint8_t ch = channel; + + adc_calibrate(); + + adc_set_regular_sequence (ADC1, 1, &ch); + + /* Start conversion on regular channels. */ + ADC_CR2 (ADC1) |= ADC_CR2_SWSTART; + + if (adc_wait (&ADC_CR2 (ADC1), ADC_CR2_SWSTART, 0, 2)) + return 0; + + if (adc_wait (&ADC_SR (ADC1), 0, ADC_SR_EOC, 2)) + return 0; + + return adc_read_regular (ADC1); +} + +void adc_init (void) +{ + /* main set ADC clock is 9Mhz */ + + adc_dump(); + + adc_off (ADC1); + rcc_periph_clock_enable (RCC_ADC1); +#if 0 + rcc_peripheral_reset (&RCC_APB2RSTR, RCC_APB2RSTR_ADC1RST); + rcc_peripheral_clear_reset (&RCC_APB2RSTR, RCC_APB2RSTR_ADC1RST); +#endif + + adc_set_dual_mode (ADC_CR1_DUALMOD_IND); + adc_disable_scan_mode (ADC1); + adc_enable_temperature_sensor (ADC1); + adc_set_single_conversion_mode (ADC1); + + adc_set_sample_time (ADC1, ADC_CHANNEL0, ADC_SMPR_SMP_239DOT5CYC); + adc_set_sample_time (ADC1, ADC_CHANNEL17, ADC_SMPR_SMP_239DOT5CYC); /*Want 17.1uS , which is 154 cycles */ + adc_enable_external_trigger_regular (ADC1, ADC_CR2_EXTSEL_SWSTART); + + adc_set_right_aligned (ADC1); + + + adc_dump(); + + adc_calibrate(); + + adc_dump(); + + + +} + diff --git a/boiler-monster/stm32/app/boiler.ld b/boiler-monster/stm32/app/boiler.ld new file mode 100644 index 0000000..e15beca --- /dev/null +++ b/boiler-monster/stm32/app/boiler.ld @@ -0,0 +1,31 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2009 Uwe Hermann + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* Linker script for Olimex STM32-H103 (STM32F103RBT6, 128K flash, 20K RAM). */ + +/* Define memory regions. */ +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K +} + +/* Include the common ld script. */ +INCLUDE libopencm3_stm32f1.ld + diff --git a/boiler-monster/stm32/app/cmd.c b/boiler-monster/stm32/app/cmd.c new file mode 100644 index 0000000..7e1975a --- /dev/null +++ b/boiler-monster/stm32/app/cmd.c @@ -0,0 +1,55 @@ +#include "project.h" + + +static void cmd_dispatch (char *cmd) +{ + + printf ("Q received cmd %s\r\n", cmd); + + if (!strncmp (cmd, "CH=", 3)) { + ot_override_ch = atoi (cmd + 3); + printf ("Q CH override set to %d\r\n", ot_override_ch); + } + + + if (!strncmp (cmd, "DHW=", 4)) { + ot_override_dhw = atoi (cmd + 4); + printf ("Q DHW override set to %d\r\n", ot_override_dhw); + } + + if (!strcmp (cmd, "PIC")) { + ot_override_dhw = atoi (cmd + 4); + printf ("Q Entering PIC mode, reset to leave\r\n"); + pic_passthru(); + } + +} + + +void cmd_usart_dispatch (void) +{ + static char cmd[16]; + static unsigned cmd_ptr; + + uint8_t c; + + if (ring_read_byte (&rx1_ring, &c)) + return; + + + if ((c == '\n') || (c == '\r')) { + if (cmd_ptr) + cmd_dispatch (cmd); + + cmd_ptr = 0; + return; + } + + + if (cmd_ptr < (sizeof (cmd) - 1)) { + cmd[cmd_ptr++] = c; + cmd[cmd_ptr] = 0; + } +} + + diff --git a/boiler-monster/stm32/app/commit.c b/boiler-monster/stm32/app/commit.c new file mode 100644 index 0000000..207448f --- /dev/null +++ b/boiler-monster/stm32/app/commit.c @@ -0,0 +1,3 @@ +#include "commit.h" + +char scm_version[] = COMMIT_ID; diff --git a/boiler-monster/stm32/app/ds1820.c b/boiler-monster/stm32/app/ds1820.c new file mode 100644 index 0000000..200f4b7 --- /dev/null +++ b/boiler-monster/stm32/app/ds1820.c @@ -0,0 +1,93 @@ +#include "project.h" + +#define DS1820_READ_SCRATCHPAD 0xbe +#define DS1820_CONVERT_T 0x44 + + +#define TIMEOUT 150 + + + +static unsigned extract_leu16 (uint8_t *d) +{ + uint32_t u; + + u = (uint32_t) d[0] | (((uint32_t) d[1]) << 8); + return u; +} + + + +static int extract_les16 (uint8_t *d) +{ + uint32_t u; + u = extract_leu16 (d); + + if (u & 0x8000) u |= 0xffff0000; + + return (int) u; +} + + + + + +static int +ds1820_read_sp (const Onewire_addr *a, unsigned page, uint8_t *buf, unsigned len) +{ + if (onewire_reset_and_select (a)) + return ~0U; + + onewire_write_byte (DS1820_READ_SCRATCHPAD); + onewire_read_bytes (buf, len); + + if ((len == 9) && onewire_check_crc (buf, 8, buf[8])) + return ~0U; + + return 0; +} + + + +static int ds1820_convert_t (const Onewire_addr *a) +{ + + if (onewire_reset_and_select (a)) + return ~0U; + + onewire_write_byte (DS1820_CONVERT_T); + + if (onewire_wait_complete (TIMEOUT)) + return ~0U; + + return 0; +} + +int +ds1820_read (const Onewire_addr *a, int *temp) +{ + uint8_t buf[9]; + int t; + + if (ds1820_read_sp (a, 0, buf, 9)) + return 1; + + if (ds1820_convert_t (a)) + return 1; + + if (ds1820_read_sp (a, 0, buf, 9)) + return 1; + + t = extract_les16 (&buf[0]); + + t <<= 4 ; + + if (temp) + *temp = t; + + + return 0; +} + + + diff --git a/boiler-monster/stm32/app/gdb.script b/boiler-monster/stm32/app/gdb.script new file mode 100644 index 0000000..7cf9d09 --- /dev/null +++ b/boiler-monster/stm32/app/gdb.script @@ -0,0 +1,2 @@ +target remote localhost:3333 +cont diff --git a/boiler-monster/stm32/app/led.c b/boiler-monster/stm32/app/led.c new file mode 100644 index 0000000..bdfd084 --- /dev/null +++ b/boiler-monster/stm32/app/led.c @@ -0,0 +1,101 @@ +#include "project.h" + +#define LED_BOARD GPIO13 +#define LED_BOARD_PORT GPIOC + +#define LED_YELLOW GPIO11 +#define LED_YELLOW_PORT GPIOA + +#define LED_GREEN1 GPIO12 +#define LED_GREEN1_PORT GPIOA + +#define LED_GREEN2 GPIO15 +#define LED_GREEN2_PORT GPIOA + +#define LED_RED GPIO3 +#define LED_RED_PORT GPIOB + +static unsigned led, yellow; + + +void led_red_set (int i) +{ + if (i) + CLEAR (LED_RED); + else + SET (LED_RED); +} + +void led_green1_set (int i) +{ + if (i) + CLEAR (LED_GREEN1); + else + SET (LED_GREEN1); +} + +void led_green2_set (int i) +{ + if (i) + CLEAR (LED_GREEN2); + else + SET (LED_GREEN2); +} + +static void _led_yellow_set (int i) +{ + if (i) + CLEAR (LED_YELLOW); + else + SET (LED_YELLOW); +} + +static void led_board_set (int i) +{ + if (i) + CLEAR (LED_BOARD); + else + SET (LED_BOARD); +} + +void led_blink (void) +{ + led = MS_TO_TICKS (25); +} + + +void led_yellow_set (int i) +{ + yellow = !!i; +} + + +void +led_tick (void) +{ + if (led) { + led--; + + led_board_set (1); + _led_yellow_set (!yellow); + } else { + + led_board_set (0); + _led_yellow_set (yellow); + } +} + + +void +led_init (void) +{ + MAP_OUTPUT_PP (LED_BOARD); + MAP_OUTPUT_PP (LED_RED); + MAP_OUTPUT_PP (LED_GREEN1); + MAP_OUTPUT_PP (LED_GREEN2); + MAP_OUTPUT_PP (LED_YELLOW); + + CLEAR (LED_BOARD); +} + + diff --git a/boiler-monster/stm32/app/main.c b/boiler-monster/stm32/app/main.c new file mode 100644 index 0000000..5ceb4af --- /dev/null +++ b/boiler-monster/stm32/app/main.c @@ -0,0 +1,134 @@ +#include "project.h" +extern uint32_t dfu_flag; + +extern volatile uint32_t vector_table[]; + + +void reset_hardware (void) +{ + int i; + + rcc_periph_reset_pulse (RST_OTGFS); + rcc_periph_reset_pulse (RST_ETHMAC); + rcc_periph_reset_pulse (RST_AFIO); + rcc_periph_reset_pulse (RST_GPIOA); + rcc_periph_reset_pulse (RST_GPIOB); + rcc_periph_reset_pulse (RST_GPIOC); + rcc_periph_reset_pulse (RST_GPIOD); + rcc_periph_reset_pulse (RST_GPIOE); + rcc_periph_reset_pulse (RST_GPIOF); + rcc_periph_reset_pulse (RST_GPIOG); + rcc_periph_reset_pulse (RST_ADC1); + rcc_periph_reset_pulse (RST_ADC2); + rcc_periph_reset_pulse (RST_TIM1); + rcc_periph_reset_pulse (RST_SPI1); + rcc_periph_reset_pulse (RST_TIM8); + rcc_periph_reset_pulse (RST_USART1); + rcc_periph_reset_pulse (RST_ADC3); + rcc_periph_reset_pulse (RST_TIM15); + rcc_periph_reset_pulse (RST_TIM16); + rcc_periph_reset_pulse (RST_TIM17); + rcc_periph_reset_pulse (RST_TIM9); + rcc_periph_reset_pulse (RST_TIM10); + rcc_periph_reset_pulse (RST_TIM11); + rcc_periph_reset_pulse (RST_TIM2); + rcc_periph_reset_pulse (RST_TIM3); + rcc_periph_reset_pulse (RST_TIM4); + rcc_periph_reset_pulse (RST_TIM5); + rcc_periph_reset_pulse (RST_TIM6); + rcc_periph_reset_pulse (RST_TIM7); + rcc_periph_reset_pulse (RST_TIM12); + rcc_periph_reset_pulse (RST_TIM13); + rcc_periph_reset_pulse (RST_TIM14); + rcc_periph_reset_pulse (RST_WWDG); + rcc_periph_reset_pulse (RST_SPI2); + rcc_periph_reset_pulse (RST_SPI3); + rcc_periph_reset_pulse (RST_USART2); + rcc_periph_reset_pulse (RST_USART3); + rcc_periph_reset_pulse (RST_UART4); + rcc_periph_reset_pulse (RST_UART5); + rcc_periph_reset_pulse (RST_I2C1); + rcc_periph_reset_pulse (RST_I2C2); + rcc_periph_reset_pulse (RST_USB); + rcc_periph_reset_pulse (RST_CAN); + rcc_periph_reset_pulse (RST_CAN1); + rcc_periph_reset_pulse (RST_CAN2); + rcc_periph_reset_pulse (RST_BKP); + rcc_periph_reset_pulse (RST_PWR); + rcc_periph_reset_pulse (RST_DAC); + rcc_periph_reset_pulse (RST_CEC); + + + for (i = 0; i < NVIC_IRQ_COUNT; ++i) + nvic_disable_irq (i); + +} + + +int +main (void) +{ + + SCB_VTOR = (uint32_t) &vector_table; + asm volatile ("msr msp, %0"::"g" (vector_table[0])); + + reset_hardware(); + + + + // rcc_clock_setup_in_hsi_out_48mhz (); + //nvic_set_priority_grouping(NVIC_PriorityGroup_4); + + /*set up pll */ + rcc_clock_setup_in_hse_8mhz_out_72mhz(); + + /*turn on clocks to periferals */ + rcc_periph_clock_enable (RCC_GPIOA); + rcc_periph_clock_enable (RCC_GPIOB); + rcc_periph_clock_enable (RCC_GPIOC); + rcc_periph_clock_enable (RCC_AFIO); + rcc_periph_clock_enable (RCC_USART1); + rcc_periph_clock_enable (RCC_USART2); + rcc_periph_clock_enable (RCC_USART3); + rcc_periph_clock_enable (RCC_ADC1); + + dwt_enable_cycle_counter(); + + /*Change interrupt priorities so that USART trumps Timer trumps ATKBD */ + nvic_set_priority (NVIC_USART1_IRQ, 0x40); + nvic_set_priority (NVIC_USART2_IRQ, 0x40); + nvic_set_priority (NVIC_USART3_IRQ, 0x40); + nvic_set_priority (NVIC_SYSTICK_IRQ, 0x80); + nvic_set_priority (NVIC_EXTI9_5_IRQ, 0xc0); + + gpio_primary_remap (AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, 0); + + led_init(); + + usart_init(); + ticker_init(); + + pic_init(); + ot_init(); + + onewire_init(); + + pressure_init(); + adc_init(); + + + printf ("STARTUP\r\n"); + printf ("%s\r\n", scm_version); + + + for (;;) { + if (!ring_empty (&rx1_ring)) + cmd_usart_dispatch(); + + temp_dispatch(); + + } + + + return 0; +} diff --git a/boiler-monster/stm32/app/ot.c b/boiler-monster/stm32/app/ot.c new file mode 100644 index 0000000..2a8ad36 --- /dev/null +++ b/boiler-monster/stm32/app/ot.c @@ -0,0 +1,452 @@ +#include "project.h" + + +typedef struct { + uint8_t flags: 5; + uint8_t type: 3; + uint8_t data[2]; +} OT_Msg; + + +#define FLAGS_PENDING_THM 1U << 0 +#define FLAGS_PROCESSED_THM 1U << 1 +#define FLAGS_PENDING_USR 1U << 2 +#define FLAGS_PROCESSED_USR 1U << 3 +#define FLAGS_PENDING_OVR 1U << 4 + +#define FLAGS_PENDING (FLAGS_PENDING_THM|FLAGS_PENDING_USR | FLAGS_PENDING_OVR) + +#define IDX_MAX 0x300 + +static OT_Msg ot_thm_req[IDX_MAX]; +static OT_Msg ot_blr_rsp[IDX_MAX]; + +#define OT_NEXT(a) (((a) < 0x2ff) ? ((a)+1):0) +#define OT_INDEX(t,id) ((((unsigned) (t) ) <<8 ) | ((unsigned) (id))) +#define OT_IDX_TO_ID(idx) ((idx) & 0xff) +#define OT_IDX_TO_TYPE(idx) ((idx) >> 8) + +static unsigned ot_req_idx; +static unsigned in_flight_req_type; +static unsigned blr_backoff; +static unsigned ot_status_wdt; + + +#define OT_READ_DATA 0x0 +#define OT_WRITE_DATA 0x1 +#define OT_INVALID_DATA 0x2 +#define OT_READ_ACK 0x4 +#define OT_WRITE_ACK 0x5 +#define OT_DATA_INVALID 0x6 +#define OT_UNKNOWN_DATAID 0x7 + + +#define OT_IDX_STATUS 0 +/*TX*/ +#define OT_IDX_STATUS_BIT_ENABLE_CH (1U << 0) +#define OT_IDX_STATUS_BIT_ENABLE_DHW (1U << 1) + +/*RX*/ +#define OT_IDX_STATUS_BIT_FAULT (1U << 0) +#define OT_IDX_STATUS_BIT_CH_MODE (1U << 1) +#define OT_IDX_STATUS_BIT_DHW_MODE (1U << 2) +#define OT_IDX_STATUS_BIT_FLAME (1U << 3) + +#define OT_IDX_CONTROL_SETPOINT 1 +#define OT_IDX_DHW_SETPOINT 56 + + +#define OT_IDX_CH_WATER_PRESSURE 18 +#define OT_IDX_RETURN_WATER_TEMP 28 +#define OT_IDX_SUPPLY_INLET_TEMP 80 + + + +unsigned ot_override_ch = 0; +unsigned ot_override_dhw = 0; + + +static inline int parity (uint8_t v) +{ + return (0x6996u >> ((v ^ (v >> 4)) & 0xf)) & 1; +} + +static void ot_parity (uint8_t *data) +{ + int p; + p = parity (data[0]); + p ^= parity (data[1]); + p ^= parity (data[2]); + p ^= parity (data[3]); + + if (p) data[0] ^= 0x80; +} + + +static const char *type_str[8] = { + ">Read Data", + ">Write Data", + ">Invalid Data", + ">Reserved", + "> 4) & 7; + + + printf ("%s%02x%02x%02x%02x %s %s\r\n", + who, + msg[0], + msg[1], + msg[2], + msg[3], + type_str[type], + what + ); +} + +static void send_reply_to_thm (unsigned idx) +{ + uint8_t reply[4]; + + if (ot_tx_thm (NULL)) return; + + reply[0] = ot_blr_rsp[idx].type << 4; + reply[1] = OT_IDX_TO_ID (idx); + reply[2] = ot_blr_rsp[idx].data[0]; + reply[3] = ot_blr_rsp[idx].data[1]; + + ot_debug ("B", reply, ""); + + ot_parity (reply); + ot_tx_thm (reply); + + ot_blr_rsp[idx].flags &= ~FLAGS_PROCESSED_THM; + +} + + +static int send_req_to_blr (unsigned idx) +{ + uint8_t req[4]; + + if (ot_tx_blr (NULL)) return -1; + + + + req[0] = ot_thm_req[ot_req_idx].type << 4; + req[1] = OT_IDX_TO_ID (ot_req_idx); + req[2] = ot_thm_req[ot_req_idx].data[0]; + req[3] = ot_thm_req[ot_req_idx].data[1]; + + ot_parity (req); + ot_debug (" S", req, ""); + + return ot_tx_blr (req); +} + + + + +void ot_rx_thm (uint8_t *msg, int error) +{ + unsigned type = (msg[0] >> 4) & 7; + unsigned id = msg[1]; + + unsigned idx; + + if (error) return; + + if (type > 2) { + ot_debug ("T", msg, "message type invalid for thermostat"); + return; + } + + if (ot_override_ch) { + if ((id == OT_IDX_STATUS) && (type == OT_READ_DATA)) /* Turn on heating */ + msg[2] |= OT_IDX_STATUS_BIT_ENABLE_CH; + + if ((id == OT_IDX_CONTROL_SETPOINT) && (type == OT_WRITE_DATA)) /* set water temp */ + msg[2] = ot_override_ch; + + } + + if (ot_override_dhw) { + if ((id == OT_IDX_STATUS) && (type == OT_READ_DATA)) /* Turn on hotwater */ + msg[2] |= OT_IDX_STATUS_BIT_ENABLE_DHW; + + if ((id == OT_IDX_DHW_SETPOINT) && (type == OT_WRITE_DATA)) /* set water temp */ + msg[2] = ot_override_dhw; + + } + + if ((id == OT_IDX_STATUS) && (type == OT_READ_DATA)) + ot_status_wdt = 0; + + ot_debug ("T", msg, ""); + + idx = OT_INDEX (type, id); + + if (ot_blr_rsp[idx].flags & FLAGS_PROCESSED_THM) + send_reply_to_thm (idx); + + else { + ot_thm_req[idx].type = type; + ot_thm_req[idx].data[0] = msg[2];; + ot_thm_req[idx].data[1] = msg[3];; + ot_thm_req[idx].flags |= FLAGS_PENDING_THM; + } +} + +static int ot_fake_read_ack (unsigned id, uint8_t *msg) +{ + unsigned t; + + switch (id) { + case OT_IDX_CH_WATER_PRESSURE: + t = pressure_ch(); + + if (!t) return -1; + + break; + + case OT_IDX_RETURN_WATER_TEMP: + t = temp_ch_return(); + + if (!t) return -1; + + break; + + case OT_IDX_SUPPLY_INLET_TEMP: + t = temp_supply_inlet(); + + if (!t) return -1; + + break; + + default: + return -1; + } + + + + msg[0] = OT_READ_ACK << 4; + msg[1] = id; + msg[2] = t >> 8; + msg[3] = t & 0xff; + + return 0; +} + + +void ot_rx_blr (uint8_t *msg, int error) +{ + unsigned type = (msg[0] >> 4) & 7; + unsigned id = msg[1]; + + unsigned idx; + + if (error) return; + + ot_debug (" A", msg, ""); + + idx = OT_INDEX (in_flight_req_type, id); + + if ((in_flight_req_type == OT_READ_DATA) && (type != OT_READ_ACK)) { + if (!ot_fake_read_ack (id, msg)) { + ot_debug (" A", msg, " (faked)"); + type = (msg[0] >> 4) & 7; + } + } + + ot_blr_rsp[idx].type = type; + ot_blr_rsp[idx].data[0] = msg[2];; + ot_blr_rsp[idx].data[1] = msg[3];; + ot_blr_rsp[idx].flags |= FLAGS_PROCESSED_THM | FLAGS_PROCESSED_USR; + + if (ot_thm_req[idx].flags & FLAGS_PENDING_THM) { + ot_thm_req[idx].flags &= ~FLAGS_PENDING_THM; + send_reply_to_thm (idx); + } + + if (ot_thm_req[idx].flags & FLAGS_PENDING_USR) { + ot_thm_req[idx].flags &= ~FLAGS_PENDING_USR; + //send_reply_to_usr (idx); + } + + if (ot_thm_req[idx].flags & FLAGS_PENDING_OVR) + ot_thm_req[idx].flags &= ~FLAGS_PENDING_OVR; + + blr_backoff = 0; + +} + + + +static void ot_boiler_worker (void) +{ + unsigned i; + + if (blr_backoff) { + blr_backoff--; + return; + } + + if (ot_tx_blr (NULL)) return; + + + for (i = 0; i < IDX_MAX; ++i, ot_req_idx = OT_NEXT (ot_req_idx)) { + + if (ot_thm_req[ot_req_idx].flags & FLAGS_PENDING) { + + if (!send_req_to_blr (ot_req_idx)) { + ot_thm_req[ot_req_idx].flags &= ~FLAGS_PENDING; + in_flight_req_type = OT_IDX_TO_TYPE (ot_req_idx); + blr_backoff = 10; + ot_req_idx = OT_NEXT (ot_req_idx); + return; + } + } + + } + +} + + +static void ot_request (unsigned flags, unsigned type, unsigned id, uint8_t *data) +{ + unsigned idx = OT_INDEX (type, id); + + ot_thm_req[idx].type = type; + + if (data) { + ot_thm_req[idx].data[0] = data[0]; + ot_thm_req[idx].data[1] = data[1]; + } else { + ot_thm_req[idx].data[0] = 0; + ot_thm_req[idx].data[1] = 0; + } + + ot_thm_req[idx].flags |= FLAGS_PENDING_USR; + +} + +void ot_request_usr (unsigned type, unsigned id, uint8_t *data) +{ + ot_request (FLAGS_PENDING_USR, type, id, data); +} +static void ot_request_ovr (unsigned type, unsigned id, uint8_t *data) +{ + ot_request (FLAGS_PENDING_OVR, type, id, data); +} + + + +static void ot_force_status (void) +{ + uint8_t data[2] = { (ot_override_ch ? OT_IDX_STATUS_BIT_ENABLE_CH : 0) | (ot_override_dhw ? OT_IDX_STATUS_BIT_ENABLE_DHW : 0), 0}; + + ot_request_ovr (OT_READ_DATA, OT_IDX_STATUS, data); + +} + +static void ot_30s_ticker (void) +{ + uint8_t data[2]; + + printf ("Q ot ticker - push control flags\r\n"); + + if (ot_override_ch) { + data[0] = ot_override_ch; + data[1] = 0; + ot_request_ovr (OT_WRITE_DATA, OT_IDX_CONTROL_SETPOINT, data); + } + + if (ot_override_dhw) { + + data[0] = ot_override_dhw; + data[1] = 0; + ot_request_ovr (OT_WRITE_DATA, OT_IDX_DHW_SETPOINT, data); + } + +} + +static void ot_2s_ticker (void) +{ + uint8_t msg[4]; + + if (!ot_fake_read_ack (OT_IDX_CH_WATER_PRESSURE, msg)) + ot_debug ("B", msg, " (fake request and reply)"); + + if (!ot_fake_read_ack (OT_IDX_RETURN_WATER_TEMP, msg)) + ot_debug ("B", msg, " (fake request and reply)"); + + if (!ot_fake_read_ack (OT_IDX_SUPPLY_INLET_TEMP, msg)) + ot_debug ("B", msg, " (fake request and reply)"); +} + + + + + +static void ot_4hz_ticker (void) +{ + static unsigned thirty, two; + thirty++; + two++; + + ot_boiler_worker(); + + ot_status_wdt++; + + if (ot_status_wdt > 120) { + printf ("Q forcing status packet\r\n"); + ot_force_status(); + ot_status_wdt = 0; + } + + + if (two >= 8) { + ot_2s_ticker(); + two = 0; + } + + + if (thirty >= 120) { + ot_30s_ticker(); + thirty = 0; + } + + + led_yellow_set (ot_blr_rsp[OT_INDEX (OT_READ_DATA, OT_IDX_STATUS)].data[1] & OT_IDX_STATUS_BIT_FAULT); + led_green1_set (ot_blr_rsp[OT_INDEX (OT_READ_DATA, OT_IDX_STATUS)].data[1] & OT_IDX_STATUS_BIT_CH_MODE); + led_green2_set (ot_blr_rsp[OT_INDEX (OT_READ_DATA, OT_IDX_STATUS)].data[1] & OT_IDX_STATUS_BIT_DHW_MODE); + led_red_set (ot_blr_rsp[OT_INDEX (OT_READ_DATA, OT_IDX_STATUS)].data[1] & OT_IDX_STATUS_BIT_FLAME); + +} + + +void ot_tick (void) +{ + static unsigned i; + i++; + + if (i >= 500) { + ot_4hz_ticker(); + i = 0; + } + + +} + + +void ot_init (void) +{ + ot_phy_rx_init(); + ot_phy_tx_init(); +} diff --git a/boiler-monster/stm32/app/ot_phy_rx.c b/boiler-monster/stm32/app/ot_phy_rx.c new file mode 100644 index 0000000..9348d24 --- /dev/null +++ b/boiler-monster/stm32/app/ot_phy_rx.c @@ -0,0 +1,177 @@ +#include "project.h" + +#define OT_THM_IN GPIO6 +#define OT_THM_IN_PORT GPIOB + +#define OT_BLR_IN GPIO7 +#define OT_BLR_IN_PORT GPIOB + + +#define OT_RX_IRQ NVIC_EXTI9_5_IRQ + + +typedef struct rx_phy { + uint32_t last_cycle; + int last_v; + unsigned half_bits; + unsigned data_bits; + int frame_error; + uint8_t data[10]; + int parity; +} RX_Phy; + + +static RX_Phy p_thm, p_blr; + + +static uint32_t +cycle_diff (uint32_t a, uint32_t b) +{ + return b - a; +} + + + + +static void ot_phy_rx_bit (RX_Phy *p) +{ + + + if (p->half_bits & 1) + p->frame_error = 1; + + if (!p->half_bits) { + if (!p->last_v) + p->frame_error = 1; + + return; + } + + if (p->data_bits < 32) { + if (p->last_v) p->data[p->data_bits >> 3] |= 0x80 >> (p->data_bits & 7); + + p->parity ^= p->last_v; + } else if (p->data_bits == 32) { + if ((!p->last_v) || (p->parity)) + p->frame_error = 1; + + led_blink(); + + if (p == &p_thm) + ot_rx_thm (p->data, p->frame_error); + else + ot_rx_blr (p->data, p->frame_error); + } + + + p->data_bits++; + +} + + +static void ot_phy_rx_worker (RX_Phy *p, int v) +{ + uint32_t now, diff; + + if (v == p->last_v) return; + + + now = dwt_read_cycle_counter(); + diff = cycle_diff (p->last_cycle, now); + + + if (diff < 10000) return; + + if (diff < 50000) { + if (! (p->half_bits & 1)) ot_phy_rx_bit (p); + + p->half_bits++; + } else if (diff < 85000) { + p->half_bits++; + ot_phy_rx_bit (p); + p->half_bits++; + } else { + p->parity = 0; + p->half_bits = 0; + p->frame_error = 0; + p->data_bits = 0; + memset (p->data, 0, sizeof (p->data)); + } + + p->last_cycle = now; + p->last_v = v; +} + + + +void +exti9_5_isr (void) +{ + int v; + + if (EXTI_PR & OT_THM_IN) { + EXTI_PR = OT_THM_IN; + v = !GET (OT_THM_IN); + +#if 0 + + if (v) + CLEAR (OT_BLR_OUT); + else + SET (OT_BLR_OUT); + +#endif + + ot_phy_rx_worker (&p_thm, v); + + + } + + if (EXTI_PR & OT_BLR_IN) { + EXTI_PR = OT_BLR_IN; + v = !GET (OT_BLR_IN); + +#if 0 + + if (v) + CLEAR (OT_THM_OUT); + else + SET (OT_THM_OUT); + +#endif + + ot_phy_rx_worker (&p_blr, v); + } + + return; +} + + +void ot_phy_rx_tick (void) +{ +} + + + +void ot_phy_rx_init (void) +{ + MAP_INPUT_PU (OT_THM_IN); + MAP_INPUT_PU (OT_BLR_IN); + + + exti_select_source (OT_THM_IN, OT_THM_IN_PORT); + exti_set_trigger (OT_THM_IN, EXTI_TRIGGER_BOTH); + exti_enable_request (OT_THM_IN); + exti_reset_request (OT_THM_IN); + + exti_select_source (OT_BLR_IN, OT_BLR_IN_PORT); + exti_set_trigger (OT_BLR_IN, EXTI_TRIGGER_BOTH); + exti_enable_request (OT_BLR_IN); + exti_reset_request (OT_BLR_IN); + + nvic_enable_irq (OT_RX_IRQ); + +} + + + diff --git a/boiler-monster/stm32/app/ot_phy_tx.c b/boiler-monster/stm32/app/ot_phy_tx.c new file mode 100644 index 0000000..36fbcc5 --- /dev/null +++ b/boiler-monster/stm32/app/ot_phy_tx.c @@ -0,0 +1,121 @@ +#include "project.h" + +#define OT_THM_OUT GPIO9 +#define OT_THM_OUT_PORT GPIOB + +#define OT_BLR_OUT GPIO8 +#define OT_BLR_OUT_PORT GPIOB + + +typedef struct tx_phy { + int busy; + unsigned half_bit; + uint8_t data[4]; +} TX_Phy; + + +static TX_Phy p_thm, p_blr; + + + +#if 0 +static uint32_t +cycle_diff (uint32_t a, uint32_t b) +{ + return b - a; +} +#endif + + +static int ot_phy_tx_worker (TX_Phy *p) +{ + int ret = p->half_bit & 1; + unsigned bit; + + + if (p->half_bit < 2) + ret ^= 1; + + else if (p->half_bit < 66) { + bit = (p->half_bit >> 1) - 1; + + if (p->data[bit >> 3] & (0x80 >> (bit & 7))) + ret ^= 1; + } else if (p->half_bit < 68) + + ret ^= 1; + + p->half_bit++; + + if (p->half_bit == 68) { + p->half_bit = 0; + p->busy = 0; + } + + return ret; +} + + +void ot_phy_tx_tick (void) +{ + int v; + + if (p_thm.busy) { + v = ot_phy_tx_worker (&p_thm); + + if (v) + CLEAR (OT_THM_OUT); + else + SET (OT_THM_OUT); + } + + + if (p_blr.busy) { + v = ot_phy_tx_worker (&p_blr); + + if (v) + CLEAR (OT_BLR_OUT); + else + SET (OT_BLR_OUT); + + } +} + + +int ot_tx_thm (uint8_t *data) +{ + if (p_thm.busy) return -1; + + if (!data) return 0; + + led_blink(); + + memcpy (p_thm.data, data, sizeof (p_thm.data)); + p_thm.busy = 1; + + return 0; +} + + +int ot_tx_blr (uint8_t *data) +{ + + if (p_blr.busy) return -1; + + if (!data) return 0; + + led_blink(); + + memcpy (p_blr.data, data, sizeof (p_blr.data)); + p_blr.busy = 1; + + return 0; +} + + + +void ot_phy_tx_init (void) +{ + MAP_OUTPUT_PP (OT_THM_OUT); + MAP_OUTPUT_PP (OT_BLR_OUT); +} diff --git a/boiler-monster/stm32/app/pic.c b/boiler-monster/stm32/app/pic.c new file mode 100644 index 0000000..b478766 --- /dev/null +++ b/boiler-monster/stm32/app/pic.c @@ -0,0 +1,52 @@ +#include "project.h" + +#define PIC_RESET GPIO1 +#define PIC_RESET_PORT GPIOB + + +static void pic_reset (void) +{ + SET (PIC_RESET); + delay_ms (1); + CLEAR (PIC_RESET); +} + + +void pic_passthru (void) +{ + uint8_t c; + + printf ("\r\nPIC MODE\r\n"); + usart1_drain(); + block_stdio = 1; + + while (!ring_read_byte (&rx3_ring, &c)); + + pic_reset(); + + + for (;;) { + if (!ring_read_byte (&rx1_ring, &c)) + usart3_queue (c); + + if (!ring_read_byte (&rx3_ring, &c)) + usart1_queue (c); + + } +} + + + + + + + +void pic_init (void) +{ + MAP_OUTPUT_PP (PIC_RESET); + + pic_reset(); + +} + + diff --git a/boiler-monster/stm32/app/pins.h b/boiler-monster/stm32/app/pins.h new file mode 100644 index 0000000..9ea01cf --- /dev/null +++ b/boiler-monster/stm32/app/pins.h @@ -0,0 +1,53 @@ +#ifndef _PINS_H_ +#define _PINS_H_ + +/* st seem to change these with every chip revision */ + +#define MAP_AF(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, a ); \ + } while (0) + +/* STM32F1 doesn't have AF pull up, but also doesn't disconnect af inputs so just use regular pull up */ +#define MAP_AF_PU(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, a); \ + gpio_set( a ## _PORT, a); \ + } while (0) + +#define MAP_AF_OD(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, a ); \ + } while (0) + + +#define MAP_OUTPUT_PP(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, a ); \ + } while (0) + + +#define MAP_OUTPUT_OD(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, a ); \ + } while (0) + + +/* STM32F1 madly uses the output register to drive the other end of the resistor, so pull up */ +/* requires us to write a 1 there */ + +#define MAP_INPUT_PU(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, a); \ + gpio_set( a ## _PORT, a); \ + } while (0) + + +#define MAP_INPUT(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, a); \ + } while (0) + +#define MAP_ANALOG(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_ANALOG, a); \ + } while (0) + + +#define CLEAR(a) gpio_clear( a ## _PORT, a) +#define SET(a) gpio_set( a ## _PORT, a) +#define GET(a) gpio_get( a ## _PORT, a) + +#endif diff --git a/boiler-monster/stm32/app/pressure.c b/boiler-monster/stm32/app/pressure.c new file mode 100644 index 0000000..f719e4d --- /dev/null +++ b/boiler-monster/stm32/app/pressure.c @@ -0,0 +1,164 @@ +#include "project.h" + +#define PRESSURE GPIO0 +#define PRESSURE_PORT GPIOA +#define PRESSURE_CHANNEL ADC_CHANNEL0 +#define VREF_CHANNEL ADC_CHANNEL17 + +#define N_SAMPLES 1000 + +#define T do { printf("%s:%d\r\n",__FILE__,__LINE__); } while (0) + +static int pressure; + +struct avg { + int acc; + unsigned n; +}; + +static void avg_reset(struct avg *a) +{ +a->acc=0; +a->n=0; +return; +} +static void avg_add(struct avg *a, int v) +{ +a->acc+=v; +a->n++; +} +inline static unsigned avg_n(struct avg *a) +{ +return a->n; +} + +static int avg_get(struct avg *a) +{ +if (!a->n) return 0; +return (a->acc) / (a->n); +} + + +uint16_t pressure_ch (void) +{ + return pressure; +} + + +static void pressure_calculate (int v,int r) +{ + /* r is 1.25 volts, transducer is 0.5V -> 0 psi 4.5V -> 100psi */ + /* 100psi is 6.8947573 bar, and we want 256ths of bar */ + + + if (!r) { + pressure = 0; + // return; + } else { + pressure = ((v * 552) / r) - 221; + + if (pressure < 0) pressure = 0; + } + + printf ("QP: %d %d %d\r\n", v, r, (pressure * 100) / 256); +} + + + +void pressure_tick(void) +{ +static unsigned state; +static unsigned backoff=MS_TO_TICKS(1000); +static unsigned timeout; + +static struct avg vref_avg; +static struct avg pressure_avg; + + +timeout++; + +if (backoff) { + backoff--; + return; +} + + +switch (state) { +case 0: + adc_off (ADC1); + adc_power_on (ADC1); + backoff=MS_TO_TICKS(5); + state++; + break; +case 1: + ADC_CR2 (ADC1) |= ADC_CR2_RSTCAL; + backoff=MS_TO_TICKS(2); + state++; + break; +case 2: + if (ADC_CR2(ADC1) & ADC_CR2_CAL) + state=0; + ADC_CR2 (ADC1) |= ADC_CR2_CAL; + backoff=MS_TO_TICKS(2); + state++; + break; +case 3: + if (ADC_CR2(ADC1) & ADC_CR2_CAL) + state=0; + state++; + + avg_reset(&vref_avg); + avg_reset(&pressure_avg); + break; +case 4: + if (adc_convert_start(VREF_CHANNEL)) + state=0; + else { + timeout=0; + state++; + } + break; +case 5: + if (adc_convert_done()) { + avg_add(&vref_avg,adc_convert_get()); + state++; + } else if (timeout > MS_TO_TICKS(5)) + state=0; + break; +case 6: + if (adc_convert_start(PRESSURE_CHANNEL)) + state=0; + else { + timeout=0; + state++; + } + break; +case 7: + if (adc_convert_done()) { + avg_add(&pressure_avg,adc_convert_get()); + + if (avg_n(&pressure_avg) MS_TO_TICKS(5)) + state=0; + break; +case 8: + pressure_calculate(avg_get(&pressure_avg),avg_get(&vref_avg)); + state=0; + break; +} + +} + + + +void pressure_init (void) +{ + MAP_ANALOG (PRESSURE); +} + + + + diff --git a/boiler-monster/stm32/app/project.h b/boiler-monster/stm32/app/project.h new file mode 100644 index 0000000..d1bfa52 --- /dev/null +++ b/boiler-monster/stm32/app/project.h @@ -0,0 +1,30 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include "ring.h" +#include "pins.h" +#include "1wire.h" + + +#define US (72) +#define MS (US * 1000) +#define HZ (MS * 1000) + +#define MS_TO_TICKS(a) ((a) *2) + + +#include "prototypes.h" diff --git a/boiler-monster/stm32/app/prototypes.h b/boiler-monster/stm32/app/prototypes.h new file mode 100644 index 0000000..5206734 --- /dev/null +++ b/boiler-monster/stm32/app/prototypes.h @@ -0,0 +1,106 @@ +/* main.c */ +extern void reset_hardware(void); +extern int main(void); +/* ot.c */ +extern unsigned ot_override_ch; +extern unsigned ot_override_dhw; +extern void ot_rx_thm(uint8_t *msg, int error); +extern void ot_rx_blr(uint8_t *msg, int error); +extern void ot_request_usr(unsigned type, unsigned id, uint8_t *data); +extern void ot_tick(void); +extern void ot_init(void); +/* ot_phy_rx.c */ +extern void exti9_5_isr(void); +extern void ot_phy_rx_tick(void); +extern void ot_phy_rx_init(void); +/* ot_phy_tx.c */ +extern void ot_phy_tx_tick(void); +extern int ot_tx_thm(uint8_t *data); +extern int ot_tx_blr(uint8_t *data); +extern void ot_phy_tx_init(void); +/* led.c */ +extern void led_red_set(int i); +extern void led_green1_set(int i); +extern void led_green2_set(int i); +extern void led_blink(void); +extern void led_yellow_set(int i); +extern void led_tick(void); +extern void led_init(void); +/* ticker.c */ +extern void delay_us(uint32_t d); +extern int tick_dispatch; +extern void sys_tick_handler(void); +extern void delay_ms(uint32_t d); +extern void ticker_init(void); +/* usart.c */ +extern ring_t rx1_ring; +extern ring_t tx1_ring; +extern ring_t rx3_ring; +extern ring_t tx3_ring; +extern void usart1_isr(void); +extern void usart1_queue(uint8_t d); +extern void usart1_drain(void); +extern int usart1_write(char *ptr, int len, int blocking); +extern void usart3_isr(void); +extern void usart3_queue(uint8_t d); +extern void usart3_drain(void); +extern int usart3_write(char *ptr, int len, int blocking); +extern void usart_init(void); +/* ring.c */ +extern void ring_init(volatile ring_t *r, uint8_t *buf, size_t len); +extern int ring_write_byte(volatile ring_t *r, uint8_t c); +extern int ring_read_byte(volatile ring_t *r, uint8_t *c); +extern int ring_write(volatile ring_t *r, uint8_t *buf, size_t len, int blocking); +extern int ring_empty(volatile ring_t *r); +/* stdio.c */ +extern unsigned block_stdio; +extern int _open(const char *name, int flags, int mode); +extern int _close(int file); +extern int _write(int file, char *buf, int nbytes); +extern int _read(int file, char *buf, int nbytes); +extern int _lseek(int file, int offset, int whence); +extern int isatty(int file); +extern void stdio_drain(void); +/* util.c */ +extern char int_to_hex_char(int a); +/* commit.c */ +extern char scm_version[]; +/* cmd.c */ +extern void cmd_usart_dispatch(void); +/* pic.c */ +extern void pic_passthru(void); +extern void pic_init(void); +/* 1wire.c */ +extern void onewire_tick(void); +extern int onewire_reset(void); +extern void onewire_write_byte(uint8_t v); +extern uint8_t onewire_read_byte(void); +extern void onewire_read_bytes(uint8_t *buf, int n); +extern void onewire_write_bytes(const uint8_t *buf, int n); +extern int onewire_select(const Onewire_addr *a); +extern int onewire_reset_and_select(const Onewire_addr *a); +extern int onewire_wait_complete(unsigned timeout); +extern int onewire_check_crc(uint8_t *buf, int n, uint8_t v); +extern int onewire_search(void); +extern void onewire_init(void); +/* temp.c */ +extern void temp_tick(void); +extern void temp_dispatch(void); +extern uint16_t temp_ch_return(void); +extern uint16_t temp_supply_inlet(void); +/* ds1820.c */ +extern int ds1820_read(const Onewire_addr *a, int *temp); +/* pressure.c */ +extern uint16_t pressure_ch(void); +extern void pressure_tick(void); +extern void pressure_init(void); +/* adc.c */ +extern void adc_dump(void); +extern volatile unsigned timeout; +extern void adc_tick(void); +extern int adc_calibrate(void); +extern int adc_convert_start(unsigned channel); +extern int adc_convert_done(void); +extern unsigned adc_convert_get(void); +extern unsigned adc_convert(unsigned channel); +extern void adc_init(void); diff --git a/boiler-monster/stm32/app/ring.c b/boiler-monster/stm32/app/ring.c new file mode 100644 index 0000000..973f345 --- /dev/null +++ b/boiler-monster/stm32/app/ring.c @@ -0,0 +1,77 @@ +#include "project.h" + + +static inline size_t +ring_next (volatile ring_t *r, size_t p) +{ + p++; + + if (p >= r->size) + p -= r->size; + + return p; +} + +void +ring_init (volatile ring_t *r, uint8_t *buf, size_t len) +{ + r->data = buf; + r->size = len; + r->write = 0; + r->read = 0; +} + +int +ring_write_byte (volatile ring_t *r, uint8_t c) +{ + size_t n = ring_next (r, r->write); + + if (n == r->read) + return -EAGAIN; + + r->data[r->write] = c; + + r->write = n; + + return 0; +} + + +int +ring_read_byte (volatile ring_t *r, uint8_t *c) +{ + size_t n = ring_next (r, r->read); + + if (r->read == r->write) + return -EAGAIN; + + *c = r->data[r->read]; + r->read = n; + + return 0; +} + +int +ring_write (volatile ring_t *r, uint8_t *buf, size_t len, int blocking) +{ + while (len--) { + if (blocking) { + while (ring_write_byte (r, *buf)); + + buf++; + } else { + if (ring_write_byte (r, * (buf++))) + return -EAGAIN; + } + } + + return 0; +} + + + +int +ring_empty (volatile ring_t *r) +{ + return (r->read == r->write) ? 1 : 0; +} diff --git a/boiler-monster/stm32/app/ring.h b/boiler-monster/stm32/app/ring.h new file mode 100644 index 0000000..a329354 --- /dev/null +++ b/boiler-monster/stm32/app/ring.h @@ -0,0 +1,13 @@ +#ifndef _RING_H_ +#define _RING_H_ + +typedef struct ring { + uint8_t *data; + size_t size; + size_t write; + size_t read; +} ring_t; + +#endif + + diff --git a/boiler-monster/stm32/app/stdio.c b/boiler-monster/stm32/app/stdio.c new file mode 100644 index 0000000..bcdd5c7 --- /dev/null +++ b/boiler-monster/stm32/app/stdio.c @@ -0,0 +1,77 @@ +#include "project.h" + +unsigned block_stdio; + +int +_open (const char *name, int flags, int mode) +{ + errno = ENOSYS; + return -1; /* Always fails */ + +} /* _open () */ + +int +_close (int file) +{ + errno = EBADF; + return -1; /* Always fails */ + +} /* _close () */ + +int +_write (int file, char *buf, int nbytes) +{ + + int ret = nbytes; + + if (!block_stdio) + ret = usart1_write (buf, nbytes, 1); + + if (ret < 0) { + errno = -ret; + return -1; + } + + return ret; +} /* _write () */ + + +int +_read (int file, char *buf, int nbytes) +{ + + errno = -EAGAIN; + return -1; /* EOF */ + +} /* _read () */ + +#if 0 +int +_fstat (int file, struct stat *st) +{ + st->st_mode = S_IFCHR; + return 0; + +} /* _fstat () */ +#endif + +int +_lseek (int file, int offset, int whence) +{ + return 0; + +} /* _lseek () */ + +int +isatty (int file) +{ + return 1; + +} /* _isatty () */ + + +void +stdio_drain (void) +{ + usart1_drain(); +} diff --git a/boiler-monster/stm32/app/temp.c b/boiler-monster/stm32/app/temp.c new file mode 100644 index 0000000..4548d01 --- /dev/null +++ b/boiler-monster/stm32/app/temp.c @@ -0,0 +1,76 @@ +#include "project.h" + + +#define N_SENSORS 2 + +#define SENSOR_INDEX_CH_RETURN 0 +#define SENSOR_INDEX_SUPPLY_INLET 1 + +static const Onewire_addr s_addr[N_SENSORS] = { + [0] = {{0x28, 0x60, 0x06, 0x53, 0x03, 0x00, 0x00, 0xf5}}, + [1] = {{0x28, 0xa4, 0x08, 0x53, 0x03, 0x00, 0x00, 0x14}}, +}; + + +static int s_temp[N_SENSORS]; + +static unsigned poke; + + +void temp_tick (void) +{ + static unsigned ticker; + + ticker++; + + if (ticker < 3000) + return; + + ticker = 0; + poke = 1; + +} + + + + + +void temp_dispatch (void) +{ + static unsigned sensor; + + if (!poke) return; + + poke = 0; + + if (sensor < N_SENSORS) { + if (ds1820_read (&s_addr[sensor], &s_temp[sensor])) + s_temp[sensor] = 0; + + + printf ("Q1W: sensor %d temp %d\r\n", sensor, (s_temp[sensor] * 100) / 256); + + + sensor++; + } else { +#if 0 + onewire_search(); +#endif + } + + if (sensor == N_SENSORS) + sensor = 0; + + +} + + + +uint16_t temp_ch_return (void) +{ + return s_temp[SENSOR_INDEX_CH_RETURN]; +} +uint16_t temp_supply_inlet (void) +{ + return s_temp[SENSOR_INDEX_SUPPLY_INLET]; +} diff --git a/boiler-monster/stm32/app/ticker.c b/boiler-monster/stm32/app/ticker.c new file mode 100644 index 0000000..9e268ca --- /dev/null +++ b/boiler-monster/stm32/app/ticker.c @@ -0,0 +1,105 @@ +#include "project.h" + + +static volatile uint32_t delay_hms_count; +static volatile uint32_t ticks; +static uint32_t scale = 7; + +void +delay_us (uint32_t d) +{ + d *= scale; + + while (d--) + __asm__ ("nop"); +} + + +void +sys_tick_handler (void) +{ + if (delay_hms_count) + delay_hms_count--; + + + led_tick(); + ot_phy_tx_tick(); + ot_tick(); + onewire_tick(); + temp_tick(); + adc_tick(); + pressure_tick(); + + + ticks++; +} + + + +void +delay_ms (uint32_t d) +{ + delay_hms_count = d << 1; + + while (delay_hms_count); +} + +#if 0 +int +timed_out (uint32_t then, unsigned int ms) +{ + then = ticks - then; + + if (then > ms) + return 1; + + return 0; +} + +int +timed_out_cycles (uint32_t then, unsigned int cycles) +{ + then = dwt_read_cycle_counter() - then; + + if (then > cycles) + return 1; + + return 0; +} +#endif + + + +void +ticker_init (void) +{ + uint32_t v, w; + + /*Start periodic timer */ + + systick_set_clocksource (STK_CSR_CLKSOURCE_AHB_DIV8); + /* 72MHz / 8 = > 9Mhz */ + systick_set_reload (4500); + /* 9MHz / 4500 => 2kHz */ + systick_interrupt_enable(); + systick_counter_enable(); + + /*Calibrate the delay loop */ + + + + do { + scale--; + v = ticks; + + while (v == ticks); + + delay_us (500); + w = ticks; + v++; + w -= v; + } while (w); + + + +} diff --git a/boiler-monster/stm32/app/usart.c b/boiler-monster/stm32/app/usart.c new file mode 100644 index 0000000..28676a0 --- /dev/null +++ b/boiler-monster/stm32/app/usart.c @@ -0,0 +1,175 @@ +#include "project.h" + +#define BUFFER_SIZE 256 + + +#define USART1_TX GPIO_USART1_TX +#define USART1_TX_PORT GPIOA + +#define USART1_RX GPIO_USART1_RX +#define USART1_RX_PORT GPIOA + +#define USART3_TX GPIO_USART3_TX +#define USART3_TX_PORT GPIOB + +#define USART3_RX GPIO_USART3_RX +#define USART3_RX_PORT GPIOB + + + +ring_t rx1_ring; +static uint8_t rx1_ring_buf[BUFFER_SIZE]; + +ring_t tx1_ring; +static uint8_t tx1_ring_buf[BUFFER_SIZE]; + +ring_t rx3_ring; +static uint8_t rx3_ring_buf[BUFFER_SIZE]; + +ring_t tx3_ring; +static uint8_t tx3_ring_buf[BUFFER_SIZE]; + +void +usart1_isr (void) +{ + uint8_t data; + + /* Check if we were called because of RXNE. */ + if (((USART_CR1 (USART1) & USART_CR1_RXNEIE) != 0) && + ((USART_SR (USART1) & USART_SR_RXNE) != 0)) { + data = usart_recv (USART1); + ring_write_byte (&rx1_ring, data); + } + + /* Check if we were called because of TXE. */ + if (((USART_CR1 (USART1) & USART_CR1_TXEIE) != 0) && + ((USART_SR (USART1) & USART_SR_TXE) != 0)) { + + if (ring_read_byte (&tx1_ring, &data)) { + /*No more data, Disable the TXE interrupt, it's no longer needed. */ + usart_disable_tx_interrupt (USART1); + } else + usart_send_blocking (USART1, data); + } + +} + +void +usart1_queue (uint8_t d) +{ + ring_write_byte (&tx1_ring, d); + usart_enable_tx_interrupt (USART1); +} + +void +usart1_drain (void) +{ + while (!ring_empty (&tx1_ring)); +} + + +int +usart1_write (char *ptr, int len, int blocking) +{ + int ret; + + ret = ring_write (&tx1_ring, (uint8_t *) ptr, len, blocking); + usart_enable_tx_interrupt (USART1); + return ret; +} + + +void +usart3_isr (void) +{ + uint8_t data; + + /* Check if we were called because of RXNE. */ + if (((USART_CR1 (USART3) & USART_CR1_RXNEIE) != 0) && + ((USART_SR (USART3) & USART_SR_RXNE) != 0)) { + data = usart_recv (USART3); + ring_write_byte (&rx3_ring, data); + } + + /* Check if we were called because of TXE. */ + if (((USART_CR1 (USART3) & USART_CR1_TXEIE) != 0) && + ((USART_SR (USART3) & USART_SR_TXE) != 0)) { + + if (ring_read_byte (&tx3_ring, &data)) { + /*No more data, Disable the TXE interrupt, it's no longer needed. */ + usart_disable_tx_interrupt (USART3); + } else + usart_send_blocking (USART3, data); + } + +} + +void +usart3_queue (uint8_t d) +{ + ring_write_byte (&tx3_ring, d); + usart_enable_tx_interrupt (USART3); +} + +void +usart3_drain (void) +{ + while (!ring_empty (&tx3_ring)); +} + + +int +usart3_write (char *ptr, int len, int blocking) +{ + int ret; + + ret = ring_write (&tx3_ring, (uint8_t *) ptr, len, blocking); + usart_enable_tx_interrupt (USART3); + return ret; +} + + +void +usart_init (void) +{ + ring_init (&rx1_ring, rx1_ring_buf, sizeof (rx1_ring_buf)); + ring_init (&tx1_ring, tx1_ring_buf, sizeof (tx1_ring_buf)); + + nvic_enable_irq (NVIC_USART1_IRQ); + + MAP_AF (USART1_TX); + MAP_AF_PU (USART1_RX); + + + usart_set_baudrate (USART1, 57600); + usart_set_databits (USART1, 8); + usart_set_stopbits (USART1, USART_STOPBITS_1); + usart_set_parity (USART1, USART_PARITY_NONE); + usart_set_flow_control (USART1, USART_FLOWCONTROL_NONE); + usart_set_mode (USART1, USART_MODE_TX_RX); + + USART_CR1 (USART1) |= USART_CR1_RXNEIE; + + usart_enable (USART1); + + + ring_init (&rx3_ring, rx3_ring_buf, sizeof (rx3_ring_buf)); + ring_init (&tx3_ring, tx3_ring_buf, sizeof (tx3_ring_buf)); + + + nvic_enable_irq (NVIC_USART3_IRQ); + + MAP_AF (USART3_TX); + MAP_AF_PU (USART3_RX); + + usart_set_baudrate (USART3, 9600); + usart_set_databits (USART3, 8); + usart_set_stopbits (USART3, USART_STOPBITS_1); + usart_set_parity (USART3, USART_PARITY_NONE); + usart_set_flow_control (USART3, USART_FLOWCONTROL_NONE); + usart_set_mode (USART3, USART_MODE_TX_RX); + + USART_CR1 (USART3) |= USART_CR1_RXNEIE; + + usart_enable (USART3); +} diff --git a/boiler-monster/stm32/app/util.c b/boiler-monster/stm32/app/util.c new file mode 100644 index 0000000..bf65510 --- /dev/null +++ b/boiler-monster/stm32/app/util.c @@ -0,0 +1,14 @@ +#include "project.h" + +char int_to_hex_char (int a) +{ + if (a < 0) return '?'; + + if (a < 0xa) return '0' + a; + + if (a < 0x10) return '7' + a; + + return '?'; +} + + diff --git a/boiler-monster/stm32/docs/pinout.txt b/boiler-monster/stm32/docs/pinout.txt new file mode 100644 index 0000000..03abe35 --- /dev/null +++ b/boiler-monster/stm32/docs/pinout.txt @@ -0,0 +1,24 @@ + +A0 <- pressure sensor +A2 <-> one wire bus +A11 -> yellow LED (OD) +A12 -> green LED 1 (OD) +A15 -> green LED 2 (OD) + +B1 -> PIC RESET +B3 -> red LED (OD) +B6 <- PIC B6 LED_C (from A0, OT from thermostat) +B7 <- PIC B7 LED_D (from A1, OT from boiler) +B8 -> PIC A6 GPIO_A (to A3, OT to boiler) +B9 -> PIC A7 GPIO_B (to A4, OT to thermostat) +B10 -> PIC RX +B11 <- PIX TX + +C13 -> onboard LED (OD) + +A2 -> MR3020 TX +A3 <- MR4020 RX +RST <- MR3020 GPIO5 + + + diff --git a/boiler-monster/stm32/docs/pm0056.pdf b/boiler-monster/stm32/docs/pm0056.pdf new file mode 100644 index 0000000..659c9de Binary files /dev/null and b/boiler-monster/stm32/docs/pm0056.pdf differ diff --git a/boiler-monster/stm32/docs/rm0008.pdf b/boiler-monster/stm32/docs/rm0008.pdf new file mode 100644 index 0000000..8f3d0e1 Binary files /dev/null and b/boiler-monster/stm32/docs/rm0008.pdf differ diff --git a/boiler-monster/stm32/docs/stm32f103c8.pdf b/boiler-monster/stm32/docs/stm32f103c8.pdf new file mode 100644 index 0000000..2723cee Binary files /dev/null and b/boiler-monster/stm32/docs/stm32f103c8.pdf differ diff --git a/boiler-monster/stm32/docs/stm32f103c8t6_pinout_voltage01.png b/boiler-monster/stm32/docs/stm32f103c8t6_pinout_voltage01.png new file mode 100644 index 0000000..2c22a74 Binary files /dev/null and b/boiler-monster/stm32/docs/stm32f103c8t6_pinout_voltage01.png differ diff --git a/boiler-monster/stm32/libopencm3 b/boiler-monster/stm32/libopencm3 new file mode 160000 index 0000000..5c73d60 --- /dev/null +++ b/boiler-monster/stm32/libopencm3 @@ -0,0 +1 @@ +Subproject commit 5c73d601763dd140bff020be8d6d06f03c2bea78 diff --git a/boiler/41hpa-warranty-registration.pdf b/boiler/41hpa-warranty-registration.pdf new file mode 100644 index 0000000..f42b06d Binary files /dev/null and b/boiler/41hpa-warranty-registration.pdf differ diff --git a/boiler/773447801-baxi-platinum-40-user.pdf b/boiler/773447801-baxi-platinum-40-user.pdf new file mode 100644 index 0000000..77c293a Binary files /dev/null and b/boiler/773447801-baxi-platinum-40-user.pdf differ diff --git a/boiler/baxi-platinum-installation-and-service-manual.pdf b/boiler/baxi-platinum-installation-and-service-manual.pdf new file mode 100644 index 0000000..20daa3c Binary files /dev/null and b/boiler/baxi-platinum-installation-and-service-manual.pdf differ diff --git a/munin/.gitignore b/munin/.gitignore new file mode 100644 index 0000000..751553b --- /dev/null +++ b/munin/.gitignore @@ -0,0 +1 @@ +*.bak diff --git a/munin/boiler b/munin/boiler new file mode 100755 index 0000000..199893c --- /dev/null +++ b/munin/boiler @@ -0,0 +1,552 @@ +#!/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/munin/boiler_rx b/munin/boiler_rx new file mode 100755 index 0000000..f794999 --- /dev/null +++ b/munin/boiler_rx @@ -0,0 +1,65 @@ +#!/usr/bin/env perl + +use Net::Telnet; +use Data::Dumper; + +sub dump_file($$) { + my ( $name, $guts ) = @_; + + $fh = new IO::File ">" . $name; + return unless defined $fh; + $fh->print($guts); + $fh->close; + undef $fh; +} + +my $timeout = 60; + +my $host = $ARGV[0]; + +my $path = "/var/run/boiler_" . $host; + +mkdir( $path, 0755 ); + +while (1) { + my $telnet = new Net::Telnet( + Timeout => 10, + Telnetmode => 0, + Binmode => 1, + Host => $host, + Port => 2001 + ); + + #$telnet->open($bits[1]); + + my $nodata = 0; + + while ( ( not $telnet->eof ) and ( $nodata < $timeout ) ) { + my $line = $telnet->getline( Timeout => 1, Errmode => 'return' ); + + if ( not defined $line or ( length($line) == 0 ) ) { + $nodata++; + } + else { + $nodata = 0; + } + + chomp $line; + chomp $line; + + $line = uc($line); + + next unless $line =~ /^([TB])(.)(.)(..)(....)/; + + my $fn = $1 . $2 . "x" . $4; + my $c = $1 . $2 . $3 . $4 . $5; + + dump_file( $path . "/" . $fn, $c ); + + } + + $telnet->close; + +} + +exit(0); diff --git a/otmonitor b/otmonitor index 6ef7f2b..ef78618 160000 --- a/otmonitor +++ b/otmonitor @@ -1 +1 @@ -Subproject commit 6ef7f2b1455ea10c5f7147216d442326c1ccfdd3 +Subproject commit ef7861864b0e2c811de3804f3742eafbbb99aa53 diff --git a/pic/.gitignore b/pic/.gitignore deleted file mode 100644 index f8ab154..0000000 --- a/pic/.gitignore +++ /dev/null @@ -1,8 +0,0 @@ -*.err -*.swp -*.swo -*.lst -*.o -*.hex -*.map -*.cod diff --git a/pic/16f88.lkr b/pic/16f88.lkr deleted file mode 100644 index b2b3e95..0000000 --- a/pic/16f88.lkr +++ /dev/null @@ -1,76 +0,0 @@ -// File: 16F88_g.lkr -// Generic linker script for the PIC16F88 processor - -#IFDEF _DEBUG - -LIBPATH . - -CODEPAGE NAME=page0 START=0x0 END=0x7FF -CODEPAGE NAME=page1 START=0x800 END=0xEFF -CODEPAGE NAME=debug START=0xF00 END=0xFFF PROTECTED -CODEPAGE NAME=.idlocs START=0x2000 END=0x2003 PROTECTED -CODEPAGE NAME=.device_id START=0x2006 END=0x2006 PROTECTED -CODEPAGE NAME=.config START=0x2007 END=0x2009 PROTECTED -CODEPAGE NAME=eedata START=0x2100 END=0x21FF PROTECTED - -DATABANK NAME=sfr0 START=0x0 END=0x1F PROTECTED -DATABANK NAME=sfr1 START=0x80 END=0x9F PROTECTED -DATABANK NAME=sfr2 START=0x100 END=0x10F PROTECTED -DATABANK NAME=sfr3 START=0x180 END=0x18F PROTECTED - -SHAREBANK NAME=dbgnobnk START=0x70 END=0x70 PROTECTED -SHAREBANK NAME=dbgnobnk START=0xF0 END=0xF0 PROTECTED -SHAREBANK NAME=dbgnobnk START=0x170 END=0x170 PROTECTED -SHAREBANK NAME=dbgnobnk START=0x1F0 END=0x1F0 PROTECTED - -SHAREBANK NAME=sfrnobnk START=0x71 END=0x7F -SHAREBANK NAME=sfrnobnk START=0xF1 END=0xFF PROTECTED -SHAREBANK NAME=sfrnobnk START=0x171 END=0x17F PROTECTED -SHAREBANK NAME=sfrnobnk START=0x1F1 END=0x1FF PROTECTED - -DATABANK NAME=gpr0 START=0x20 END=0x6F -DATABANK NAME=gpr1 START=0xA0 END=0xEF -DATABANK NAME=gpr2 START=0x110 END=0x16F -DATABANK NAME=gpr3 START=0x190 END=0x1E4 -DATABANK NAME=dbg3 START=0x1E5 END=0x1EF PROTECTED - -SECTION NAME=PROG0 ROM=page0 // ROM code space -SECTION NAME=PROG1 ROM=page1 // ROM code space -SECTION NAME=DEBUG ROM=debug // ICD debug executive -SECTION NAME=IDLOCS ROM=.idlocs // ID locations -SECTION NAME=DEVICEID ROM=.device_id // Device ID -SECTION NAME=DEEPROM ROM=eedata // Data EEPROM - -#ELSE - -LIBPATH . - -CODEPAGE NAME=page0 START=0x0 END=0x7FF -CODEPAGE NAME=page1 START=0x800 END=0xFFF -CODEPAGE NAME=.idlocs START=0x2000 END=0x2003 PROTECTED -CODEPAGE NAME=.device_id START=0x2006 END=0x2006 PROTECTED -CODEPAGE NAME=.config START=0x2007 END=0x2009 PROTECTED -CODEPAGE NAME=eedata START=0x2100 END=0x21FF PROTECTED - -DATABANK NAME=sfr0 START=0x0 END=0x1F PROTECTED -DATABANK NAME=sfr1 START=0x80 END=0x9F PROTECTED -DATABANK NAME=sfr2 START=0x100 END=0x10F PROTECTED -DATABANK NAME=sfr3 START=0x180 END=0x18F PROTECTED - -SHAREBANK NAME=sfrnobnk START=0x70 END=0x7F -SHAREBANK NAME=sfrnobnk START=0xF0 END=0xFF PROTECTED -SHAREBANK NAME=sfrnobnk START=0x170 END=0x17F PROTECTED -SHAREBANK NAME=sfrnobnk START=0x1F0 END=0x1FF PROTECTED - -DATABANK NAME=gpr0 START=0x20 END=0x6F -DATABANK NAME=gpr1 START=0xA0 END=0xEF -DATABANK NAME=gpr2 START=0x110 END=0x16F -DATABANK NAME=gpr3 START=0x190 END=0x1EF - -SECTION NAME=PROG0 ROM=page0 // ROM code space -SECTION NAME=PROG1 ROM=page1 // ROM code space -SECTION NAME=IDLOCS ROM=.idlocs // ID locations -SECTION NAME=DEVICEID ROM=.device_id // Device ID -SECTION NAME=DEEPROM ROM=eedata // Data EEPROM - -#FI diff --git a/pic/Makefile b/pic/Makefile deleted file mode 100644 index 31ab11c..0000000 --- a/pic/Makefile +++ /dev/null @@ -1,22 +0,0 @@ -AFLAGS=-k -p 16F88 -k -LDFLAGS=-m -s 16f88.lkr - -PROG=gateway -ASRCS=${PROG}.asm selfprog.asm -OBJS=${ASRCS:%.asm=%.o} -LSTS=${ASRCS:%.asm=%.err} -ERRS=${ASRCS:%.asm=%.lst} - -default:${PROG}.hex - -%.o:%.asm - gpasm ${AFLAGS} -c -o $@ $< - -${PROG}.hex:${OBJS} - gplink ${LDFLAGS} -o $@ ${OBJS} - - -clean: - /bin/rm -f ${PROG}.hex ${PROG}.map ${PROG}.cod ${OBJS} ${LSTS} ${ERRS} - - diff --git a/pic/build.asm b/pic/build.asm deleted file mode 100644 index 67d0747..0000000 --- a/pic/build.asm +++ /dev/null @@ -1,2 +0,0 @@ -#define build "1" -#define tstamp "17:59 20-10-2015" diff --git a/pic/docs/otgw-big-sch.png b/pic/docs/otgw-big-sch.png deleted file mode 100644 index 114e65b..0000000 Binary files a/pic/docs/otgw-big-sch.png and /dev/null differ diff --git a/pic/docs/pinout.txt b/pic/docs/pinout.txt deleted file mode 100644 index a3db674..0000000 --- a/pic/docs/pinout.txt +++ /dev/null @@ -1,33 +0,0 @@ - -headers: - -left - -GND - STM32 -GPIO_B <- STM32 B9 -GPIO_A <- STM32 B8 -5V -> STM32 -LED_D -> STM32 B7 -LED_C -> STM32 B6 -LED_B -> NC -LED_A -> NC - - -middle - -GND - NC -NC - NC -NC - -PIC RX <- STM32 B10 -PIC TX -> STM32 B11 -*RESET <- STM32 B1 - - -right - -5V -> MR3020 -NC -GND - MR3020 - - - diff --git a/pic/gateway.asm b/pic/gateway.asm deleted file mode 100644 index b1c6324..0000000 --- a/pic/gateway.asm +++ /dev/null @@ -1,296 +0,0 @@ - title "OpenTherm Gateway" - list p=16F88, b=8, r=dec - -;Copyright (c) 2009 Schelte Bron - -#define version "4.2" -#define phase "." ;a=alpha, b=beta, .=production -#define patch "5" ;Comment out when not applicable -;#define bugfix "1" ;Comment out when not applicable -#include build.asm - -;See the file "license.txt" for information on usage and redistribution of -;this file, and for a DISCLAIMER OF ALL WARRANTIES. - -#ifndef LVP - __config H'2007', B'10111101110100' -#else - __config H'2007', B'10111111110100' -#endif - __config H'2008', B'11111111111111' - - errorlevel -302 - -;####################################################################### -;Comparator 1 is used for requests. The input is connected to the thermostat. -;In monitor mode, the output is connected to the boiler. -;Comparator 2 is used for responses. The input is connected to the Boiler. -;In monitor mode, the output is connected to the thermostat. - -;The AUSART is configured for full duplex asynchronous serial communication at -;9600 baud, 8 bits, no parity. It is used for reporting about opentherm messages -;and receiving commands. - -;Analog input 0 is used to measure the voltage level on the opentherm line to -;the thermostat. This way the code can distinguish between a real opentherm -;thermostat and a simple on/off thermostat. An opentherm thermostat will keep -;the line at a few volts (low) or between 15 and 18 volts (high). An on/off -;thermostat will either short-circuit the line (0 volts) or leave the line open -;(20+ volts). - -#include "p16f88.inc" - -;Define the speed of the PIC clock -#define mhzstr "4" - -;BAUD contains the SPBRG value for 9600 baud - constant BAUD=25 - - extern SelfProg - -;Variables accessible from all RAM banks - UDATA_SHR -loopcounter res 1 - -Bank0data UDATA 0x20 -rxbuffer res 16 ;Serial receive buffer - -;Variables for longer lasting storage -rxpointer res 1 -txpointer res 1 - -temp res 1 - -Bank1data UDATA 0xA0 - -Bank1values UDATA 0xE0 - -Bank2data UDATA 0x110 -Bank2values UDATA 0x160 - -Bank3data UDATA 0x190 -;Use all available RAM in bank 3 for the transmit buffer - constant TXBUFSIZE=80 -txbuffer res TXBUFSIZE -Bank3values UDATA 0x1E0 - -;I/O map -#define MasterIn CMCON,C1OUT -#define SlaveIn CMCON,C2OUT -#define SlaveOut PORTA,3 -#define MasterOut PORTA,4 -#define RXD PORTB,2 -#define TXD PORTB,5 -#define CCP1 PORTB,0 -#define LED_A PORTB,3 -#define LED_B PORTB,4 -#define LED_C PORTB,6 -#define LED_D PORTB,7 -#define RSET PORTB,1 ;Used by self-programming - -#define SlaveMask b'00001000' -#define MasterMask b'00010000' - -package macro pkg -pkg code -pkg - endm - -pcall macro rtn - lcall rtn - pagesel $ - endm - -;Skip the next instruction (bit 7 of PCLATH is always 0) -skip macro - btfsc PCLATH,7 - endm - -;Get the output of the active comparator into the carry bit -getcompout macro - bsf STATUS,RP0 - rlf CMCON,W ;Get the output of comparator 2 - bcf STATUS,RP0 - btfsc Request ;A request goes through comparator 1 - addlw b'10000000' ;Cause a carry if C1OUT is high - endm - -;The first thing to do upon a reset is to allow the firmware to be updated. -;So no matter how buggy freshly loaded firmware is, if the first two command -;have not been messed up, the device can always be recovered without the need -;for a PIC programmer. The self-programming code times out and returns after -;a second when no firmware update is performed. -; - -ResetVector code 0x0000 - lcall SelfProg ;Always allow a firmware update on boot - lgoto Start ;Start the opentherm gateway program - -InterruptVector code 0x0004 - retfie ;End of interrupt routine - - package Interrupt - -;######################################################################## -; Main program -;######################################################################## - - package Main -Break tstf RCREG ;Clear the RCIF interrupt flag - bcf STATUS,RP1 - bcf STATUS,RP0 ;bank 0 - movlw 128 - call Pause - pcall SelfProg ;Jump to the self-programming code -Start bcf STATUS,RP1 - bcf STATUS,RP0 ;bank 0 - clrf PORTB ;Flash the LED's on startup - bsf STATUS,RP0 - movlw b'01100000' ;Internal oscillator set to 4MHz - movwf OSCCON ;Configure the oscillator - - ;Configure I/O pins - ;Power on defaults all pins to inputs - ;Port A - ;Pins 0 and 1 are used as comparator inputs - ;Pin 2 is used as VREF (must be configured as input!) - ;Pins 3 and 4 are (comparator) ouputs - ;Pin 5 is master reset input - ;Pins 6 and 7 are GPIO - movlw b'11100111' - movwf TRISA - - ;Port B - ;Pins 2 and 5 are used by the USART and don't need to be configured - ;Pins 3, 4, 6, and 7 are used to indicate events for debugging -#ifndef LVP - movlw b'00100111' -#else - ;Can't use RB3 if LVP is enabled - movlw b'00101111' -#endif - movwf TRISB - - movlw b'11' - movwf PCON - - ;Configure Timer 0 & Watchdog Timer - ;Bit 7 RBPU = 1 - Port B pull up disabled - ;Bit 6 INTEDG = 1 - Interrupt on rising edge - ;Bit 5 T0CS = 0 - Internal clock - ;Bit 4 T0SE = 1 - High to low transition - ;Bit 3 PSA = 0 - Prescaler assigned to Timer 0 - ;Bit 2-0 PS = 5 - 1:64 Prescaler - movlw b'11010101' - movwf OPTION_REG - - - ;Configure comparator module - ;Bit 7 C2OUT = R/O - ;Bit 6 C1OUT = R/O - ;Bit 5 C2INV = 0 - Not inverted - ;Bit 4 C1INV = 0 - Not inverted - ;Bit 3 CIS = 0 - Irrelevant - ;Bit 2-0 = 3 - Common reference / 6 - with outputs - movlw b'00000011' ;Common reference mode - movwf CMCON - - movlw b'00000111' ;A0 through A2 are used for analog I/O - movwf ANSEL - - ;Configure the serial interface - movlw BAUD - movwf SPBRG - bsf TXSTA,BRGH ;9600 baud - bcf TXSTA,SYNC ;Asynchronous - bsf TXSTA,TXEN ;Enable transmit - bcf STATUS,RP0 - bsf RCSTA,SPEN ;Enable serial port - bsf RCSTA,CREN ;Enable receive - - ;Configure A/D converter - movlw b'01000001' ;A/D on, channel 0, Fosc/8 - movwf ADCON0 - - ;Configure the voltage reference module - ;The reference voltage must be set to 1.3V - ;Bit 7 VREN = 1 - VREF Enabled - ;Bit 6 VROE = 1 - Output on pin RA2 - ;Bit 5 VRR = 1 - Low range - ;Bit 3-0 VR = 6 - 1.25V (7 - 1.46V) - movlw b'11100110' - bsf STATUS,RP0 - movwf CVRCON ;Set the reference voltage - - - clrf STATUS - bcf PIR2,CMIF ;Clear any comparator interrupt - - movlw 0x41 - movwf TXREG - - - - -MainLoop clrwdt - - ; Copy CMCON bits 7 and 6 from to PORTB bits 7 and 6 (comparitor outputs to LED_C and LED_D pins) - bsf STATUS,RP0 - movfw CMCON - bcf STATUS,RP0 - - andlw b'11000000' - iorwf PORTB,F - iorlw b'00111111' - andwf PORTB,F - - ; Copy PORTA bits 7 and 6 to PORTA bits 4 and 3 (GPIO inputs to transmitter outputs) - rrf PORTA,W - movwf temp - rrf temp,F - rrf temp,W - - andlw b'00011000' - iorwf PORTA,F - iorlw b'11100111' - andwf PORTA,F - - ; And port B bits 4 and 3 (LED_A and LED_B) - - andlw b'00011000' - iorwf PORTB,F - iorlw b'11100111' - andwf PORTB,F - - - btfss PIR1,RCIF ;Activity on the serial receiver? - goto MainLoop - tstf RCREG - - ;FERR is only relevant if RCIF is set - btfsc RCSTA,FERR ;Check for framing error (break) - goto Break - btfss RCSTA,OERR ;Check for overrun error - goto serial_recv - bcf RCSTA,CREN ;Procedure to clear an overrun error - bsf RCSTA,CREN ;Re-enable the serial interface -serial_recv movfw RCREG - ;movwf TXREG - xorlw 0x4 - skpnz - goto Break - goto MainLoop - -Pause clrwdt - btfsc PIR1,RCIF - return - btfss INTCON,TMR0IF - goto Pause - bcf INTCON,TMR0IF - addlw -1 - skpz - goto Pause - return - - - end diff --git a/pic/selfprog.asm b/pic/selfprog.asm deleted file mode 100644 index 9e18769..0000000 --- a/pic/selfprog.asm +++ /dev/null @@ -1,391 +0,0 @@ - title "Self Programming" - list b=8, r=dec - -;######################################################################## -; Self programming, based on Microchip AN851 -;######################################################################## -; This code has been optimized for size so it would fit in 0x100 code words. -; To achieve this, some special tricks had to be used. As a result the code -; may be a little hard to read -; -; The functionality differs from that described in AN851 in the following -; respects: -; 1. No auto baud detection. Communication must take place at 9600 baud. -; 2. Dropped the first that was used for auto baud detection. -; 3. Dropped the third address byte as it would always be 0. -; 4. The code does not check the last byte of EEPROM data memory to enter boot -; mode. Instead it transmits a character and waits up to 1 second for -; a character. -; 5. The code is designed to be placed in the highest part of memory. That way -; it doesn't interfere with the interrupt code address. It does rely on the -; main program to call the self-program code at an appropriate time. -; 6. Due to the location of the boot loader code, it cannot be protected using -; the code protection bits in the configuration word. This means that the -; boot loader code can also be updated, if necessary. -; 7. The Erase Flash command has been implemented for the PIC16F device. -; 8. The device reset command is 0x08, not any command with a 0 length. -; 9. The version command can be called with a data length of 1-3 words. It will -; report the following information: -; 1. Version -; 2. Boot loader start address -; 3. Boot loader end address -; The software used to reflash the device can use the last two pieces of -; information to determine which part of memory should not be touched. - -#include "p16f88.inc" - - errorlevel -302, -306 - -#define MAJOR_VERSION 1 -#define MINOR_VERSION 1 - -#define STX 0x0F -#define ETX 0x04 -#define DLE 0x05 - -CHKSUM equ 0x70 -COUNTER equ 0x71 -RXDATA equ 0x72 -TXDATA equ 0x73 -TEMP equ 0x74 - -DATA_BUFF equ 0x10 ;Start of receive buffer -COMMAND equ 0x10 ;Data mapped in receive buffer -DATA_COUNT equ 0x11 -ADDRESS_L equ 0x12 -ADDRESS_H equ 0x13 -PACKET_DATA equ 0x14 - -#define SELFRESET PORTB,1 -#define RX PORTB,2 -#define TX PORTB,5 - -#ifdef SELFPROGNEW - #define SELFPROG SelfProgNew - #define STARTADDRESS 0x0700 -#else - #define SELFPROG SelfProg - #define STARTADDRESS 0x0f00 -#endif - - global SELFPROG -SELFPROG code STARTADDRESS - ;Do not go into selfprog mode after a watchdog timeout reset -SELFPROG btfss STATUS,NOT_TO - return ;Let the main code handle a timeout - - bcf INTCON,GIE ;Ignore interrupts - - banksel OSCCON - movlw b'01100000' ;Internal oscillator set to 4MHz - movwf OSCCON ;Configure the oscillator - - ;Set the LEDS as outputs -#ifndef LVP - movlw b'00100111' -#else - movlw b'00101111' -#endif - movwf TRISB - - ;Configure the comparators and voltage reference - ;Allow communication between thermostat and boiler to continue - ;while reprogramming the device - movlw b'11100111' ;Pins 3 and 4 are digital ouputs - movwf TRISA - movlw b'00000111' ;A0 through A2 are used for analog I/O - movwf ANSEL - movlw b'11100110' ;Voltage reference at 1.25V - movwf CVRCON - movlw b'00000110' ;Two common reference comps with output - movwf CMCON - - ;Configure Timer 0 & Watchdog Timer - ;Bit 7 RBPU = 1 - Port B pull up disabled - ;Bit 6 INTEDG = 1 - Interrupt on rising edge - ;Bit 5 T0CS = 0 - Internal clock - ;Bit 4 T0SE = 1 - High to low transition - ;Bit 3 PSA = 0 - Prescaler assigned to Timer 0 - ;Bit 2-0 PS = 7 - 1:256 Prescaler - movlw b'11010111' - movwf OPTION_REG - - movlw 25 ;9600 baud - movwf SPBRG - movlw b'00100110' - movwf TXSTA ;High speed, Asynchronous, Enabled - bcf STATUS,RP0 - bsf RCSTA,SPEN ;Enable serial port - bsf RCSTA,CREN ;Enable receive - - ;Clear the LEDS - movlw b'11111111' - movwf PORTB - - clrf TMR0 - movlw 1 - call Pause -;Notify the external programming software (Pause returns ) - call WrRS232 -;Programmer should react within 1 second - movlw 16 - call Pause - btfss PIR1,RCIF -;No data received, resume the normal program - return -;Check that the received character is - call RdRS232 - skpz -;Other serial data received, resume normal program - return - - bsf STATUS,IRP -ReSync movlw DATA_BUFF - movwf FSR - clrf CHKSUM -GetNextDat call RdRS232 ;Get the data - skpnz - goto ReSync ;Found unprotected STX - xorlw STX ^ ETX ;Check for ETX - skpnz - goto CheckSum ;Yes, examine checksum - xorlw ETX ^ DLE ;Check for DLE - skpnz - call RdRS232 ;Yes, get the next byte - movfw RXDATA - movwf INDF ;Store the data - addwf CHKSUM,F ;Get sum - incf FSR,F - goto GetNextDat - -CheckSum tstf CHKSUM - skpz - goto StartOfLine - bsf STATUS,RP1 - movfw ADDRESS_L - movwf EEADR - movfw ADDRESS_H - movwf EEADRH - movlw PACKET_DATA - movwf FSR - movfw DATA_COUNT - movwf COUNTER -CheckCommand -#ifdef SELFPROGNEW - movlw high ($ + 0x800) -#else - movlw high $ -#endif - movwf PCLATH - movfw COMMAND - sublw 8 - sublw 8 - skpc - goto StartOfLine - addwf PCL,F - goto ReadVersion ;0 Read Version Information - goto ReadProgMem ;1 Read Program Memory - goto WriteProgMem ;2 Write Program Memory - goto EraseProgMem ;3 Erase Program Memory - goto ReadEE ;4 Read EEDATA Memory - goto WriteEE ;5 Write EEDATA Memory - goto StartOfLine ;6 Read Config Memory - goto StartOfLine ;7 Write Config Memory - goto VReset ;8 Reset - -VersionData data (MAJOR_VERSION << 8) | MINOR_VERSION -#ifdef SELFPROGNEW - data SELFPROG + 0x800, SelfProgEnd + 0x800 -#else - data SELFPROG, SelfProgEnd -#endif - -ReadVersion movlw low VersionData - movwf EEADR -#ifdef SELFPROGNEW - movlw high (VersionData + 0x800) -#else - movlw high VersionData -#endif - movwf EEADRH - movlw DATA_BUFF + 2 - movwf FSR -ReadProgMem bsf STATUS,RP0 - bsf EECON1,EEPGD - bsf EECON1,RD - nop - nop - bcf STATUS,RP0 - movfw EEDATA - movwf INDF - incf FSR,F - movfw EEDATH - movwf INDF - incf FSR,F - incf EEADR,F - skpnz - incf EEADRH,F - decfsz COUNTER,F - goto ReadProgMem -WritePacket movlw DATA_BUFF - subwf FSR,W -WritePacketJ1 movwf COUNTER - movlw STX - call WrRS232 - clrf CHKSUM - movlw DATA_BUFF - movwf FSR -SendNext movfw INDF - addwf CHKSUM,F - incf FSR,F - call WrData - decfsz COUNTER,F - goto SendNext - comf CHKSUM,W - addlw 1 - call WrData ;WrData returns - call WrRS232 -StartOfLine bcf STATUS,RP1 - call RdRS232 ;Look for a start of line - skpnz - goto ReSync - goto StartOfLine - -WriteProgMem movlw b'10000100' - call LoadEECon1 - movlw b'11111100' - andwf EEADR,F - movlw 4 - movwf TEMP -Lp1 movfw INDF - movwf EEDATA - incf FSR,F - movfw INDF - movwf EEDATH - incf FSR,F - call StartWrite - decfsz TEMP,F - goto Lp1 - decfsz COUNTER,F - goto WriteProgMem - goto WritePacketJ1 - -EraseProgMem movlw b'10010100' - call LoadEECon1 - movlw 0x1F - iorwf EEADR,F - call StartWrite - decfsz COUNTER,F - goto EraseProgMem - goto WritePacketJ1 - -ReadEE bsf STATUS,RP0 - clrf EECON1 - bsf EECON1,RD - bcf STATUS,RP0 - movfw EEDATA - movwf INDF - incf FSR,F - incf EEADR,F - decfsz COUNTER,F - goto ReadEE - goto WritePacket - -WriteEE movlw b'00000100' - call LoadEECon1 - movfw INDF - movwf EEDATA - incf FSR,F - call StartWrite - decfsz COUNTER,F - goto WriteEE - goto WritePacketJ1 - -WrData movwf TXDATA - xorlw STX - skpnz - goto WrDLE - xorlw STX ^ ETX - skpnz - goto WrDLE - xorlw ETX ^ DLE - skpz - goto WrNext -WrDLE movlw DLE - call WrRS232 -WrNext movfw TXDATA -WrRS232 clrwdt - bcf STATUS,RP1 -WaitTxEmpty btfss PIR1,TXIF - goto WaitTxEmpty - movwf TXREG - retlw ETX - -RdRS232 btfsc RCSTA,OERR - goto VReset -RdLp1 clrwdt - btfss PIR1,RCIF - goto RdLp1 - movfw RCREG - movwf RXDATA - xorlw STX - return - -LoadEECon1 bsf STATUS,RP0 - movwf EECON1 - bcf STATUS,RP0 - return - -StartWrite clrwdt - bsf STATUS,RP0 - movlw 0x55 - movwf EECON2 - movlw 0xAA - movwf EECON2 - bsf EECON1,WR -WaitEEWrite btfsc EECON1,WR ;Skipped when writing program memory - goto WaitEEWrite ;Skipped when writing program memory - bcf STATUS,RP0 - incf EEADR,F - skpnz - incf EEADRH,F - retlw 1 ;Return length of acknowledgement - -Pause clrwdt - btfsc PIR1,RCIF - return - btfss INTCON,TMR0IF - goto Pause - bcf INTCON,TMR0IF - addlw -1 - skpz - goto Pause - retlw ETX - -;Reset the device via an external connection from an I/O pin to the reset pin. -VReset bcf STATUS,RP1 - bcf SELFRESET ;Prepare the output latch - bsf STATUS,RP0 - bcf SELFRESET ;Switch the pin to output - -;If resetting via the output pin doesn't work, restore all used registers to -;their power-on defaults and jump to address 0. -;Do not simply return because the code that called this routine may have been -;wiped and reprogrammed to something completely different. - movlw b'11111111' - movwf TRISA - movwf TRISB - movwf OPTION_REG - movwf ANSEL - movlw b'00000111' - movwf CMCON - clrf TXSTA - clrf SPBRG - clrf STATUS - clrf RCSTA - clrf INTCON - clrf PCLATH - clrwdt -SelfProgEnd goto 0 - - end diff --git a/radiator-plc/DOCS/DS18B20.pdf b/radiator-plc/DOCS/DS18B20.pdf new file mode 100644 index 0000000..52dc259 Binary files /dev/null and b/radiator-plc/DOCS/DS18B20.pdf differ diff --git a/radiator-plc/DOCS/plc_notes.txt b/radiator-plc/DOCS/plc_notes.txt new file mode 100644 index 0000000..89ce2a2 --- /dev/null +++ b/radiator-plc/DOCS/plc_notes.txt @@ -0,0 +1,93 @@ + +STM32 pin assignments + +PA0 X4 +PA1 X3 +PA2 RS422 pin 4 +PA3 RS422 pin 1 +PA4 +PA5 +PA6 RS422 pin 3 +PA7 Via 472 to Run switch +PA8 Y1 +PA9 RS232 +PA10 RS232 +PA11 Y2 +PA12 Y3 +PA13 SWDIO +PA14 SWCLK +PA15 + +PB0 10k to ground +PB1 AD1 via condiitoning (conditioning removed (apart from zener) and with 10k pull up) +PB2 AD0 via conditioning +PB3 +PB4 +PB5 +PB6 +PB7 +PB8 +PB9 +PB10 +PB11 +PB12 +PB13 +PB14 +PB15 X2 + +PC0 +PC1 +PC2 Err LED +PC3 Run LED +PC4 X5 +PC5 +PC6 X1 +PC7 X0 +PC8 24V detect +PC9 Y0 +PC10 +PC11 +PC12 +PC13 tamper RTC +PC14 OSC32 (no sign of xtal) +PC15 OSC32 (no sign of xtal) + +PD0 Y4 +PD1 Y5 +PD2 Y6 +PD3 Y7 +PD4 +PD5 +PD6 +PD7 +PD8 +PD9 +PD10 X13 +PD11 X12 +PD12 X11 +PD13 X10 +PD14 X7 +PD15 X6 + +PE0 +PE1 +PE2 +PE3 +PE4 +PE5 +PE6 +PE7 +PE8 +PE9 +PE10 +PE11 +PE12 +PE13 +PE14 +PE15 + +VBAT +BOOT0 strapped by R16/R72 + + + diff --git a/radiator-plc/DOCS/radiator-numbering.txt b/radiator-plc/DOCS/radiator-numbering.txt new file mode 100644 index 0000000..dabb942 --- /dev/null +++ b/radiator-plc/DOCS/radiator-numbering.txt @@ -0,0 +1,8 @@ +0: small radiator next to fireplace +1: dd nearest road +2: dd nearest garden +3: hall +4: kitchen +5: music room +6: bathroom + diff --git a/radiator-plc/DOCS/valve_01213210.pdf b/radiator-plc/DOCS/valve_01213210.pdf new file mode 100644 index 0000000..3ad1c10 Binary files /dev/null and b/radiator-plc/DOCS/valve_01213210.pdf differ diff --git a/radiator-plc/DOCS/valve_wiring.txt b/radiator-plc/DOCS/valve_wiring.txt new file mode 100644 index 0000000..882aa63 --- /dev/null +++ b/radiator-plc/DOCS/valve_wiring.txt @@ -0,0 +1,15 @@ +Pin numbers on RJ45 and DB-9 are the same + +1 or/w 1 wire bus +2 or GND +3 g/w uSwitch (short to gound to indicate valve open) +4 bl 5V +5 bl/w 24V +6 g uSwitch +7 br/w coil - plc pulls to ground to open valve +8 br coil + +(doubling up is to make wirign in plc neater) + + + diff --git a/radiator-plc/mt300n-v2/.gitignore b/radiator-plc/mt300n-v2/.gitignore new file mode 100644 index 0000000..859afb1 --- /dev/null +++ b/radiator-plc/mt300n-v2/.gitignore @@ -0,0 +1 @@ +stamp diff --git a/radiator-plc/mt300n-v2/Makefile b/radiator-plc/mt300n-v2/Makefile new file mode 100644 index 0000000..cdf9d24 --- /dev/null +++ b/radiator-plc/mt300n-v2/Makefile @@ -0,0 +1,6 @@ +STUFF=$(shell find usr \! -type d -print) + +stamp: ${STUFF} + tar cf - ${STUFF} | ssh radiator-plc0 "cd / && tar xvpf -" + #tar cf - ${STUFF} | ssh radiator-plc1 "cd / && tar xvpf -" + touch $@ diff --git a/radiator-plc/mt300n-v2/usr/bin/mqtt_if.pl b/radiator-plc/mt300n-v2/usr/bin/mqtt_if.pl new file mode 100755 index 0000000..f81b6e0 --- /dev/null +++ b/radiator-plc/mt300n-v2/usr/bin/mqtt_if.pl @@ -0,0 +1,76 @@ +#!/usr/bin/env perl + +use strict; + +use Device::SerialPort; +use Net::MQTT::Simple; + +sub chomp_harder($) { + my $ret = shift; + my $irs = $/; + $/ = "\n"; + chomp $ret; + $/ = "\r"; + chomp $ret; + $/ = $irs; + return $ret; +} + +sub get_line($$) { + my ( $port, $mqtt ) = @_; + + my $ret = ""; + + while (1) { + my ( $count, $byte ) = $port->read(1); + $mqtt->tick(); + + if ( $count > 0 ) { + $ret .= $byte; + return chomp_harder($ret) if $byte eq "\n"; + } + else { + $ret = chomp_harder($ret); + return $ret; + } + } +} + +my $plc_port = "/dev/ttyS1"; +my $plc = new Device::SerialPort($plc_port) || die "can't open $plc_port\n"; + +$plc->baudrate(38400); +$plc->parity("none"); +$plc->databits(8); +$plc->stopbits(1); +$plc->stty_icanon(0); + +$plc->read_char_time(0); +$plc->read_const_time(1000); + +my $mqtt = Net::MQTT::Simple->new("10.32.139.1"); + +sub mqtt_msg($$) { + my ( $topic, $message ) = @_; + + $plc->write("MQTT $topic $message\r\n"); +} + +$mqtt->subscribe( 'cmnd/+/var1', \&mqtt_msg ); +$mqtt->subscribe( 'cmnd/+/var2', \&mqtt_msg ); +$mqtt->subscribe( 'cmnd/+/var3', \&mqtt_msg ); + +while (1) { + my $line = get_line( $plc, $mqtt ); + next unless length($line) > 0; + + if ( $line =~ /MQTT\s+([^\s]+)\s+(.+)/ ) { + $mqtt->publish( $1, $2 ); + } + + if ( $line =~ /MQTTR\s+([^\s]+)\s+(.+)/ ) { + $mqtt->retain( $1, $2 ); + } + +} + diff --git a/radiator-plc/mt300n-v2/usr/bin/run_mqtt_if b/radiator-plc/mt300n-v2/usr/bin/run_mqtt_if new file mode 100755 index 0000000..2c06f6b --- /dev/null +++ b/radiator-plc/mt300n-v2/usr/bin/run_mqtt_if @@ -0,0 +1,6 @@ +#!/bin/sh + +while true; do + /usr/local/bin/mqtt_if.pl + sleep 1 +done diff --git a/radiator-plc/stm32/.gitignore b/radiator-plc/stm32/.gitignore new file mode 100644 index 0000000..64532f4 --- /dev/null +++ b/radiator-plc/stm32/.gitignore @@ -0,0 +1,15 @@ +*.o +*.d +*.map +*.hex +*.elf +*.swp +*~ +*.dfu +plot/data +plot/script +*.bin +scmversion +commit.h +opencm3.build.stamp +*.orig diff --git a/radiator-plc/stm32/Makefile b/radiator-plc/stm32/Makefile new file mode 100644 index 0000000..e1cd497 --- /dev/null +++ b/radiator-plc/stm32/Makefile @@ -0,0 +1,8 @@ + +default: + make -C libopencm3 + make -C app + +clean: + make -C libopencm3 clean + make -C app clean diff --git a/radiator-plc/stm32/Makefile.include b/radiator-plc/stm32/Makefile.include new file mode 100644 index 0000000..4f5cbd9 --- /dev/null +++ b/radiator-plc/stm32/Makefile.include @@ -0,0 +1,44 @@ +## +## This file is part of the libopencm3 project. +## +## Copyright (C) 2009 Uwe Hermann +## Copyright (C) 2010 Piotr Esden-Tempski +## +## This library is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU Lesser General Public License for more details. +## +## You should have received a copy of the GNU Lesser General Public License +## along with this library. If not, see . +## + +LIBNAME = opencm3_stm32f1 +DEFS = -DSTM32F1 + +FP_FLAGS ?= -msoft-float +ARCH_FLAGS = -mthumb -mcpu=cortex-m3 $(FP_FLAGS) -mfix-cortex-m3-ldrd + +################################################################################ +# OpenOCD specific variables + +OOCD ?= openocd +OOCD_INTERFACE ?= interface/stlink-v2.cfg +OOCD_BOARD ?= target/stm32f1x.cfg + +################################################################################ +# Black Magic Probe specific variables +# Set the BMP_PORT to a serial port and then BMP is used for flashing +BMP_PORT ?= + +################################################################################ +# texane/stlink specific variables +#STLINK_PORT ?= :4242 + + +include ../Makefile.rules diff --git a/radiator-plc/stm32/Makefile.rules b/radiator-plc/stm32/Makefile.rules new file mode 100644 index 0000000..a723e6f --- /dev/null +++ b/radiator-plc/stm32/Makefile.rules @@ -0,0 +1,261 @@ +# +## This file is part of the libopencm3 project. +## +## Copyright (C) 2009 Uwe Hermann +## Copyright (C) 2010 Piotr Esden-Tempski +## Copyright (C) 2013 Frantisek Burian +## +## This library is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU Lesser General Public License for more details. +## +## You should have received a copy of the GNU Lesser General Public License +## along with this library. If not, see . +## + +# Be silent per default, but 'make V=1' will show all compiler calls. +ifneq ($(V),1) +Q := @ +NULL := 2>/dev/null +endif + +############################################################################### +# Executables + +PREFIX ?= arm-none-eabi + +CC := $(PREFIX)-gcc +CXX := $(PREFIX)-g++ +LD := $(PREFIX)-gcc +AR := $(PREFIX)-ar +AS := $(PREFIX)-as +OBJCOPY := $(PREFIX)-objcopy +OBJDUMP := $(PREFIX)-objdump +GDB := $(PREFIX)-gdb +STFLASH = $(shell which st-flash) +STYLECHECK := /checkpatch.pl +STYLECHECKFLAGS := --no-tree -f --terse --mailback +STYLECHECKFILES := $(shell find . -name '*.[ch]') + + +############################################################################### +# Source files + +LDSCRIPT ?= $(BINARY).ld + +#OBJS += $(BINARY).o + + +ifeq ($(strip $(OPENCM3_DIR)),) +# user has not specified the library path, so we try to detect it + +# where we search for the library +LIBPATHS := ./libopencm3 ../libopencm3 + +OPENCM3_DIR := $(wildcard $(LIBPATHS:=/locm3.sublime-project)) +OPENCM3_DIR := $(firstword $(dir $(OPENCM3_DIR))) + +ifeq ($(strip $(OPENCM3_DIR)),) +$(warning Cannot find libopencm3 library in the standard search paths.) +$(error Please specify it through OPENCM3_DIR variable!) +endif +endif + +ifeq ($(V),1) +$(info Using $(OPENCM3_DIR) path to library) +endif + +INCLUDE_DIR = $(OPENCM3_DIR)/include +LIB_DIR = $(OPENCM3_DIR)/lib +SCRIPT_DIR = $(OPENCM3_DIR)/scripts + +############################################################################### +# C flags + +CFLAGS += -Os -g +CFLAGS += -Wextra -Wimplicit-function-declaration +CFLAGS += -Wmissing-prototypes -Wstrict-prototypes +CFLAGS += -fno-common -ffunction-sections -fdata-sections + +############################################################################### +# C++ flags + +CXXFLAGS += -Os -g +CXXFLAGS += -Wextra -Wshadow -Wredundant-decls -Weffc++ +CXXFLAGS += -fno-common -ffunction-sections -fdata-sections + +############################################################################### +# C & C++ preprocessor common flags + +CPPFLAGS += -MD +CPPFLAGS += -Wall -Wundef + +INCLUDES = -I$(INCLUDE_DIR) +DEFINES = $(DEFS) + +CPPFLAGS += $(INCLUDES) $(DEFINES) + +############################################################################### +# Linker flags + +LDFLAGS += --static -nostartfiles +LDFLAGS += -L$(LIB_DIR) +LDFLAGS += -T$(LDSCRIPT) +LDFLAGS += -Wl,-Map=$(*).map +LDFLAGS += -Wl,--gc-sections +ifeq ($(V),99) +LDFLAGS += -Wl,--print-gc-sections +endif + +############################################################################### +# Used libraries + +LDLIBS += -l$(LIBNAME) +LDLIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group + +############################################################################### +############################################################################### +############################################################################### + +.SUFFIXES: .elf .bin .hex .srec .list .map .images .dfu +.SECONDEXPANSION: +.SECONDARY: + +all: elf + + +elf: $(BINARY).elf +bin: $(BINARY).bin +hex: $(BINARY).hex +srec: $(BINARY).srec +list: $(BINARY).list + +images: $(BINARY).images +flash: $(BINARY).flash + +%.images: %.bin %.hex %.srec %.list %.map %.dfu + @#printf "*** $* images generated ***\n" + +%.bin: %.elf + @#printf " OBJCOPY $(*).bin\n" + $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).bin + +%.hex: %.elf + @#printf " OBJCOPY $(*).hex\n" + $(Q)$(OBJCOPY) -Oihex $(*).elf $(*).hex + +%.dfu: %.elf + @#printf " OBJCOPY $(*).dfu\n" + $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).dfu + +%.srec: %.elf + @#printf " OBJCOPY $(*).srec\n" + $(Q)$(OBJCOPY) -Osrec $(*).elf $(*).srec + +%.list: %.elf + @#printf " OBJDUMP $(*).list\n" + $(Q)$(OBJDUMP) -S $(*).elf > $(*).list + +fish: + echo %.elf %.map: $(OBJS) $(LDSCRIPT) $(LIB_DIR)/lib$(LIBNAME).a + echo $(BINARY).elf + +%.elf %.map: $(OBJS) $(LDSCRIPT) $(LIB_DIR)/lib$(LIBNAME).a + @#printf " LD $(*).elf\n" + $(Q)$(LD) $(LDFLAGS) $(ARCH_FLAGS) $(OBJS) $(LDLIBS) -o $(*).elf + +%.o: %.c $(OPENCM3_DIR)/../opencm3.build.stamp + @#printf " CC $(*).c\n" + $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $(*).o -c $(*).c + +%.o: %.cxx $(OPENCM3_DIR)/../opencm3.build.stamp + @#printf " CXX $(*).cxx\n" + $(Q)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $(*).o -c $(*).cxx + +%.o: %.cpp $(OPENCM3_DIR)/../opencm3.build.stamp + @#printf " CXX $(*).cpp\n" + $(Q)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $(*).o -c $(*).cpp + +clean: + @#printf " CLEAN\n" + $(Q)$(RM) *.o *.d *.elf *.bin *.hex *.srec *.list *.map *~ *.dfu ${EXTRACLEANS} + +stylecheck: $(STYLECHECKFILES:=.stylecheck) +styleclean: $(STYLECHECKFILES:=.styleclean) + +# the cat is due to multithreaded nature - we like to have consistent chunks of text on the output +%.stylecheck: % + $(Q)$(SCRIPT_DIR)$(STYLECHECK) $(STYLECHECKFLAGS) $* > $*.stylecheck; \ + if [ -s $*.stylecheck ]; then \ + cat $*.stylecheck; \ + else \ + rm -f $*.stylecheck; \ + fi; + +%.styleclean: + $(Q)rm -f $*.stylecheck; + + +%.stlink-flash: %.bin + @printf " FLASH $<\n" + $(Q)$(STFLASH) write $(*).bin 0x8000000 + +ifeq ($(STLINK_PORT),) +ifeq ($(BMP_PORT),) +ifeq ($(OOCD_SERIAL),) +%.flash: %.hex + @printf " FLASH $<\n" + @# IMPORTANT: Don't use "resume", only "reset" will work correctly! + $(Q)$(OOCD) -f $(OOCD_INTERFACE) \ + -f $(OOCD_BOARD) \ + -c "init" -c "reset init" \ + -c "flash write_image erase $(*).hex" \ + -c "reset" \ + -c "shutdown" $(NULL) +else +%.flash: %.hex + @printf " FLASH $<\n" + @# IMPORTANT: Don't use "resume", only "reset" will work correctly! + $(Q)$(OOCD) -f $(OOCD_INTERFACE) \ + -f $(OOCD_BOARD) \ + -c "ft2232_serial $(OOCD_SERIAL)" \ + -c "init" -c "reset init" \ + -c "flash write_image erase $(*).hex" \ + -c "reset" \ + -c "shutdown" $(NULL) +endif +else +%.flash: %.elf + @printf " GDB $(*).elf (flash)\n" + $(Q)$(GDB) --batch \ + -ex 'target extended-remote $(BMP_PORT)' \ + -x $(SCRIPT_DIR)/black_magic_probe_flash.scr \ + $(*).elf +endif +else +%.flash: %.elf + @printf " GDB $(*).elf (flash)\n" + $(Q)$(GDB) --batch \ + -ex 'target extended-remote $(STLINK_PORT)' \ + -x $(SCRIPT_DIR)/stlink_flash.scr \ + $(*).elf +endif + +.PHONY: images clean stylecheck styleclean elf bin hex srec list + +-include $(OBJS:.o=.d) + + +$(LIB_DIR)/lib$(LIBNAME).a: $(OPENCM3_DIR)/../opencm3.build.stamp + + +$(OPENCM3_DIR)/../opencm3.build.stamp: + ${MAKE} -C ${OPENCM3_DIR} + touch $@ + diff --git a/radiator-plc/stm32/app/1wire.c b/radiator-plc/stm32/app/1wire.c new file mode 100644 index 0000000..dd66e92 --- /dev/null +++ b/radiator-plc/stm32/app/1wire.c @@ -0,0 +1,384 @@ +#include "project.h" + +#define GPIO_PORT GPIOB +#define GPIO GPIO1 + +#define BIT_ZERO '0' +#define BIT_ONE '1' +#define BIT_UNKNOWN 'U' +#define BIT_CONFLICT 'C' + + +static inline void +low (void) +{ + CLEAR (GPIO); +} + +static inline void +hiz (void) +{ + SET (GPIO); +} + +static inline int +get (void) +{ + return ! !GET (GPIO); +} + +/*Returns 1 if timeout - waits ~100ms */ +static inline int +wait_hiz (void) +{ + unsigned int timeout = 10000; + hiz(); + + while (!get()) { + timeout--; + + if (! (timeout--)) + return 1; + + delay_us (10); + } + + return 0; +} + + +/* returns 1 if nothing on bus or error */ +int +onewire_reset (void) +{ + int ret; + cm_disable_interrupts(); + low(); + delay_us (600); + hiz(); + delay_us (65); + ret = get(); + + if (wait_hiz()) + ret = 1; + + delay_us (100); + cm_enable_interrupts(); + return ret; +} + +void +onewire_one (void) +{ + cm_disable_interrupts(); + low(); + delay_us (10); + hiz(); + delay_us (90); + cm_enable_interrupts(); +} + +void +onewire_zero (void) +{ + cm_disable_interrupts(); + low(); + delay_us (90); + hiz(); + delay_us (10); + cm_enable_interrupts(); +} + +int +onewire_read (void) +{ + int ret; + cm_disable_interrupts(); + low(); + delay_us (10); + hiz(); + delay_us (20); + ret = get(); + delay_us (70); + cm_enable_interrupts(); + return ret; +} + + +void +onewire_write_byte (uint8_t v) +{ + int c; + + for (c = 0; c < 8; ++c) { + if (v & 1) + onewire_one(); + else + onewire_zero(); + + v >>= 1; + } +} + +uint8_t +onewire_read_byte (void) +{ + uint8_t v = 0; + int c; + + for (c = 0; c < 8; ++c) { + v >>= 1; + + if (onewire_read()) + v |= 0x80; + } + + + return v; +} + +void +onewire_read_bytes (uint8_t *buf, int n) +{ + while (n--) + * (buf++) = onewire_read_byte(); +} + + +void +onewire_write_bytes (const uint8_t *buf, int n) +{ + while (n--) + onewire_write_byte (* (buf++)); +} + + +int onewire_select (const Onewire_addr *a) +{ + if (!a) + onewire_write_byte (ONEWIRE_SKIP_ROM); + else { + onewire_write_byte (ONEWIRE_MATCH_ROM); + onewire_write_bytes (a->a, 8); + } + + return 0; +} + + +int onewire_reset_and_select (const Onewire_addr *a) +{ + if (onewire_reset()) + return 1; + + if (onewire_select (a)) + return 1; + + return 0; +} + + + +int onewire_wait_complete (unsigned timeout) +{ + while (! (onewire_read())) { + delay_ms (10); + + if (! (timeout--)) + return 1; + } + + return 0; +} + + + +int +onewire_check_crc (uint8_t *buf, int n, uint8_t v) +{ + uint8_t crc = 0; + int i; + + while (n--) { + uint8_t v = * (buf++); + + for (i = 0; i < 8; ++i) { + uint8_t mix = (crc ^ v) & 0x01; + crc >>= 1; + + if (mix) + crc ^= 0x8C; + + v >>= 1; + } + } + + return ! (crc == v); + +} + +static int onewire_conduct_search (uint8_t *bits) +{ + unsigned i, ir, r; + + if (onewire_reset()) + return -1; + + onewire_write_byte (ONEWIRE_SEARCH_ROM); + + + for (i = 0; i < 64; ++i) { + + r = onewire_read(); + ir = onewire_read(); + +#if 0 + + if ((i == 27) || (i == 24) || (i == 39)) + ir = r = 0; + +#endif + + switch (bits[i]) { + case BIT_UNKNOWN: + + if (!r && ir) { /* Zero */ + bits[i] = BIT_ZERO; + onewire_zero(); + } else if (r && !ir) { /*One */ + bits[i] = BIT_ONE; + onewire_one(); + } else if (r && ir) /*Nothing here */ + return -1; + + else if (!r && !ir) { /*Both */ + bits[i] = BIT_CONFLICT; + onewire_zero(); + } + + break; + + case BIT_CONFLICT: + if (!r && !ir) /*Both */ + onewire_zero(); + else + return -1; + + break; + + case BIT_ZERO: + if (!r) + onewire_zero(); + else + return -1; + + break; + + case BIT_ONE: + if (!ir) + onewire_one(); + else + return -1; + + break; + } + } + + return 0; +} + +static int onewire_next (uint8_t *bits) +{ + unsigned i; + + for (i = 0; i < 64; ++i) { + + if (bits[63 - i] == BIT_CONFLICT) { + bits[63 - i] = BIT_ONE; + return 1; + } + + bits[63 - i] = BIT_UNKNOWN; + } + + return 0; +} + +static void onewire_bits_to_a (uint8_t *bits, Onewire_addr *a) +{ + unsigned i, j, c; + + for (i = 0, j = 0; i < 8; ++i) { + + a->a[i] = 0; + + for (c = 1; c < 0x100; c <<= 1, ++j) { + if (bits[j] == BIT_ONE) + a->a[i] |= c; + } + } +} + +int onewire_search (void) +{ + uint8_t bits[64]; + Onewire_addr a; + int r, c; + + + printf ("One wire search\n"); + //delay_ms (2000); + + memset (bits, BIT_UNKNOWN, sizeof (bits)); + + do { + r = onewire_conduct_search (bits); + + if (!r) { + onewire_bits_to_a (bits, &a); + c = onewire_check_crc (&a.a[0], 7, a.a[7]); + + if (!c) { + printf ("O: {0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x}\r\n", + a.a[0], + a.a[1], + a.a[2], + a.a[3], + a.a[4], + a.a[5], + a.a[6], + a.a[7]); + } + + } + + } while (onewire_next (bits)); + + + + + + + return 0; +} + + +void +onewire_init (void) +{ + MAP_OUTPUT_OD_SLOW (GPIO); + + hiz(); + delay_us (20); + printf ("hiz -> %d\n", get()); + + low(); + + delay_us (20); + printf ("low -> %d\n", get()); + + hiz(); + delay_us (20); + + +} diff --git a/radiator-plc/stm32/app/1wire.h b/radiator-plc/stm32/app/1wire.h new file mode 100644 index 0000000..edfc1b9 --- /dev/null +++ b/radiator-plc/stm32/app/1wire.h @@ -0,0 +1,9 @@ +#define ONEWIRE_SKIP_ROM 0xcc +#define ONEWIRE_SEARCH_ROM 0xf0 +#define ONEWIRE_MATCH_ROM 0x55 + +#define ONEWIRE_ADDR_LEN 8 +typedef struct { + uint8_t a[ONEWIRE_ADDR_LEN]; +} Onewire_addr; + diff --git a/radiator-plc/stm32/app/Makefile b/radiator-plc/stm32/app/Makefile new file mode 100644 index 0000000..8ef06f7 --- /dev/null +++ b/radiator-plc/stm32/app/Makefile @@ -0,0 +1,59 @@ +## +## This file is part of the libopencm3 project. +## +## Copyright (C) 2009 Uwe Hermann +## +## This library is free software: you can redistribute it and/or modify +## it under the terms of the GNU Lesser General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## This library is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU Lesser General Public License for more details. +## +## You should have received a copy of the GNU Lesser General Public License +## along with this library. If not, see . +## + +CPROTO=cproto +PROG=radiator-plc + +D=radiator-plc0 + +V=1 +default: ${PROG}.elf + +CSRCS=hexdump.c main.c ring.c ticker.c usart.c watchdog.c led.c 1wire.c inputs.c outputs.c logic.c ds18b20.c + +HSRCS= 1wire.h project.h ring.h + +BINARY = ${PROG} +OBJS = ${CSRCS:%.c=%.o} + +${OBJS}: + +include ../Makefile.include + +INCLUDES += -I.. + +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) \ + -f $(OOCD_BOARD) \ + -c "init" -c "reset init" \ + -c "flash write_image erase $<" \ + -c "reset" \ + -c "shutdown" $(NULL) + +tidy: + astyle -A3 -s2 --attach-extern-c -L -c -w -Y -m0 -f -p -H -U -k3 -xj -xd ${CSRCS} ${HSRCS} + diff --git a/radiator-plc/stm32/app/control.c b/radiator-plc/stm32/app/control.c new file mode 100644 index 0000000..f3a35c9 --- /dev/null +++ b/radiator-plc/stm32/app/control.c @@ -0,0 +1,114 @@ +#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/ds18b20.c b/radiator-plc/stm32/app/ds18b20.c new file mode 100644 index 0000000..3bd63ed --- /dev/null +++ b/radiator-plc/stm32/app/ds18b20.c @@ -0,0 +1,128 @@ +#include "project.h" + +#define DS18B20_CONVERT 0x44 +#define DS18B20_WRITE_SCRATCHPAD 0x4e +#define DS18B20_READ_SCRATCHPAD 0xbe + + +#define TIMEOUT 150 + + +unsigned extract_leu16 (uint8_t *d) +{ + uint32_t u; + + u = (uint32_t) d[0] | (((uint32_t) d[1]) << 8); + return u; +} + + +int extract_les16 (uint8_t *d) +{ + uint32_t u; + u = extract_leu16 (d); + + if (u & 0x8000) u |= 0xffff0000; + + return (int) u; +} + + +/* do not call from interrupt context (ie ticker)*/ +int +ds18b20_read_sp (const Onewire_addr *a, uint8_t *buf, unsigned len) +{ + if (onewire_reset_and_select (a)) + return ~0U; + + onewire_write_byte (DS18B20_READ_SCRATCHPAD); + onewire_read_bytes (buf, len); + + + if ((len == 9) && onewire_check_crc (buf, 8, buf[8])) + return ~0U; + + return 0; +} + +int +ds18b20_write_sp (const Onewire_addr *a, uint8_t *buf, unsigned len) +{ + if (onewire_reset_and_select (a)) + return ~0U; + + onewire_write_byte (DS18B20_WRITE_SCRATCHPAD); + onewire_write_bytes (buf, len); + + return 0; +} + +int ds18b20_convert (const Onewire_addr *a, unsigned timeout) +{ + + if (onewire_reset_and_select (a)) + return ~0U; + + onewire_write_byte (DS18B20_CONVERT); + + if (timeout && onewire_wait_complete (timeout)) + return ~0U; + + return 0; +} + +int +ds18b20_set_12 (const Onewire_addr *a) +{ + uint8_t buf[9]; + + + if (ds18b20_read_sp (a, buf, 9)) + return 1; + + buf[0] = 0x0; + buf[1] = 0x0; + buf[2] = 0x7f; /* 12 bit conversions */ + + return ds18b20_write_sp (a, buf, 3); + +} + +int ds18b20_read (const Onewire_addr *a, int *temp) +{ + uint8_t buf[9]; + + if (ds18b20_read_sp (a, buf, 9)) + return 1; + + *temp = (extract_les16 (buf) * 500) >> 3; + return 0; +} + + +int +ds18b20_set_12_convert (const Onewire_addr *a) +{ + + if (ds18b20_set_12 (a)) + return 1; + + if (ds18b20_convert (a, 0)) + return 1; + + return 0; +} + +/* do not call from interrupt context (ie ticker)*/ +int +ds18b20_set_12_convert_and_read (const Onewire_addr *a, int *temp) +{ + if (ds18b20_set_12 (a)) + return 1; + + if (ds18b20_convert (a, TIMEOUT)) + return 1; + + return ds18b20_read (a, temp); +} + diff --git a/radiator-plc/stm32/app/gdb.script b/radiator-plc/stm32/app/gdb.script new file mode 100644 index 0000000..69f0239 --- /dev/null +++ b/radiator-plc/stm32/app/gdb.script @@ -0,0 +1,2 @@ +target remote boreas:3333 +cont diff --git a/radiator-plc/stm32/app/hexdump.c b/radiator-plc/stm32/app/hexdump.c new file mode 100644 index 0000000..83f02ea --- /dev/null +++ b/radiator-plc/stm32/app/hexdump.c @@ -0,0 +1,57 @@ +#include "project.h" + +void +hexdump (void *_d, int len) +{ + uint8_t *d = (uint8_t *) _d; + int i, j, k; + int e; + + if (!d || len < 0) + return; + + e = len + 15; + e &= ~15; + + for (i = 0; i < e; i += 16) { + usart1_drain(); + printf (" %05x:", i); + + for (j = 0; j < 16; ++j) { + k = i + j; + + if (k < len) + printf (" %02x", d[k]); + else + printf (" "); + + if (j == 7) + printf (" "); + } + + printf (" "); + + for (j = 0; j < 16; ++j) { + k = i + j; + + if (k < len) { + uint8_t c = d[k]; + + if (c < 33) + c = '.'; + + if (c > 126) + c = '.'; + + printf ("%c", c); + } + + if (j == 7) + printf (" "); + } + + printf ("\r\n"); + } + + usart1_drain(); +} diff --git a/radiator-plc/stm32/app/inputs.c b/radiator-plc/stm32/app/inputs.c new file mode 100644 index 0000000..825a3fe --- /dev/null +++ b/radiator-plc/stm32/app/inputs.c @@ -0,0 +1,26 @@ +#include "project.h" + +#define N_INPUTS 14 + +static const uint32_t input_bank[N_INPUTS] = { GPIOC, GPIOC, GPIOB, GPIOA, GPIOA, GPIOC, GPIOD, GPIOD, 0, 0, GPIOD, GPIOD, GPIOD, GPIOD}; +static const uint32_t input[N_INPUTS] = { GPIO7, GPIO6, GPIO15, GPIO1, GPIO0, GPIO4, GPIO15, GPIO14, 0, 0, GPIO13, GPIO12, GPIO11, GPIO10}; + +int input_get (unsigned r) +{ + if (r >= N_INPUTS) return 0; + + if (!input[r]) return 0; + + return !gpio_get (input_bank[r], input[r]); +} + + +void inputs_init (void) +{ + unsigned r; + + for (r = 0; r < N_INPUTS; ++r) + if (input[r]) + gpio_set_mode (input_bank[r], GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, input[r]); + +} diff --git a/radiator-plc/stm32/app/led.c b/radiator-plc/stm32/app/led.c new file mode 100644 index 0000000..390ed2e --- /dev/null +++ b/radiator-plc/stm32/app/led.c @@ -0,0 +1,53 @@ +#include "project.h" + +#define LED0 GPIO3 +#define LED0_PORT GPIOC + +#define LED1 GPIO2 +#define LED1_PORT GPIOC + +static int led = 0; + +void +led_init (void) +{ + MAP_OUTPUT_PP (LED0); + MAP_OUTPUT_PP (LED1); + + SET (LED0); + SET (LED1); +} + +void led1_set (void) +{ + CLEAR (LED1); +} + +void led1_clear (void) +{ + SET (LED1); +} + + + +void +led_tick (void) +{ + static unsigned i; + + i++; + + if (i < 1000) return; + + i = 0; + + led ^= 1; + + if (!led) + CLEAR (LED0); + + if (led) + SET (LED0); + +} + diff --git a/radiator-plc/stm32/app/logic.c b/radiator-plc/stm32/app/logic.c new file mode 100644 index 0000000..8cab7ca --- /dev/null +++ b/radiator-plc/stm32/app/logic.c @@ -0,0 +1,158 @@ +#include "project.h" + + +#define N_VALVES 8 + +static volatile int semaphore = 0; + + + +static const char *valve_name[N_VALVES] = { + "dd_radiator1", + "dd_radiator2", + "dd_radiator3", + "hall_radiator", + "kitchen_radiator", + "music_room_radiator", + "bathroom_radiator", + "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}}, + {{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}}, +}; +volatile int temp[N_VALVES]; + +#define BAD_TEMP 0x7fffffff + +int low_limit[N_VALVES] = { + 15000, + 15000, + 15000, + 15000, + 15000, + 15000, + 15000, + 00000, +}; + + +int high_limit[N_VALVES] = { + 15000, + 15000, + 15000, + 15000, + 15000, + 15000, + 15000, + 00000, +}; + +int valve_state[N_VALVES]; + + + +void mqtt_dispatch (char *type, char *who, char *what, char *msg) +{ + unsigned i; + + if (strcmp (type, "cmnd")) return; + + for (i = 0; i < N_VALVES; ++i) { + + if (!strcmp (who, valve_name[i])) { + if (!strcmp (what, "var1")) + low_limit[i] = atoi (msg) * 1000; + + if (!strcmp (what, "var2")) + high_limit[i] = atoi (msg) * 1000; + } + } +} + + +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 char msg_buf[80]; + int t; + int o; + int v; + + + + if (!read_failed) + read_failed = ds18b20_read (&valve_owa[i], &t); + + if (read_failed) { + temp[i] = BAD_TEMP; + valve_state[i] = 0; + } else { + temp[i] = t; + + if (t < low_limit[i]) + valve_state[i] = 1; + + if (t >= high_limit[i]) + valve_state[i] = 0; + } + + o = valve_state[i]; + + output_write (i, o); + v = input_get (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/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]))); + } + + + i++; + + if (i >= N_VALVES) + i = 0; + + if (request_search) { + request_search = 0; + onewire_search(); + } + + read_failed = ds18b20_set_12_convert (&valve_owa[i]); +} + + +void logic_slow_tick (void) +{ + semaphore = 1; +} + + +void logic_dispatch (void) +{ +} diff --git a/radiator-plc/stm32/app/main.c b/radiator-plc/stm32/app/main.c new file mode 100644 index 0000000..57b6752 --- /dev/null +++ b/radiator-plc/stm32/app/main.c @@ -0,0 +1,150 @@ +#include "project.h" + + +volatile int request_search; + +static void mqtt_cmd (char *type) +{ + char *m; + char *who, *what; + + m = rindex (type, ' '); + + if (!m) return; + + *m = 0; + m++; + + while (index (type, ' ') == type) + type++; + + who = index (type, ' '); + if (who) *who = 0; + + + who = index (type, '/'); + + if (!who) { + who = ""; + what = ""; + } else { + *who = 0; + who++; + + what = index (who, '/'); + + if (!what) + what = ""; + + else { + *what = 0; + what++; + } + } + + + mqtt_dispatch (type, who, what, m); +} + +static void cmd_dispatch (char *cmd) +{ + + if (!strcmp (cmd, "SEARCH")) { + request_search = 1; + return; + } + + if (!strncmp (cmd, "MQTT ", 5)) { + mqtt_cmd (cmd + 5); + return; + } + + + printf ("Unknown command %s\n", cmd); +} + + + + +static void +kbd_dispatch (void) +{ + static char cmd_buf[80]; + static unsigned cmd_ptr; + + uint8_t c; + + while (!ring_read_byte (&rx1_ring, &c)) { + + switch (c) { + case ' ': + case '\t': + if (!cmd_ptr) + break; + + /*fall through */ + default: + cmd_buf[cmd_ptr++] = c; + cmd_buf[cmd_ptr] = 0; + + + if (cmd_ptr < (sizeof (cmd_buf) - 1)) + break; + + /*fall through*/ + case '\r': + case '\n': + if (!cmd_ptr) + break; + + cmd_dispatch (cmd_buf); + cmd_ptr = 0; + break; + } + } +} + + +int +main (void) +{ + /*set up pll */ + rcc_clock_setup_in_hse_8mhz_out_72mhz(); + + /*turn on clocks which aren't done elsewhere */ + rcc_periph_clock_enable (RCC_GPIOA); + rcc_periph_clock_enable (RCC_GPIOB); + rcc_periph_clock_enable (RCC_GPIOC); + rcc_periph_clock_enable (RCC_GPIOD); + rcc_periph_clock_enable (RCC_GPIOE); + rcc_periph_clock_enable (RCC_AFIO); + + //watchdog_init (); + + /*Adjust interrupt priorities so that uarts trump timer */ + nvic_set_priority (NVIC_USART1_IRQ, 0x40); + nvic_set_priority (NVIC_USART2_IRQ, 0x40); + nvic_set_priority (NVIC_USART3_IRQ, 0x40); + nvic_set_priority (NVIC_SYSTICK_IRQ, 0xff); + + ticker_init(); + usart_init(); + led_init(); + onewire_init(); + inputs_init(); + outputs_init(); + + printf ("D: RESET\n"); + + //onewire_search(); + + for (;;) { + if (!ring_empty (&rx1_ring)) + kbd_dispatch(); + + logic_dispatch(); + + } + + return 0; +} diff --git a/radiator-plc/stm32/app/outputs.c b/radiator-plc/stm32/app/outputs.c new file mode 100644 index 0000000..67d0519 --- /dev/null +++ b/radiator-plc/stm32/app/outputs.c @@ -0,0 +1,47 @@ +#include "project.h" + +#define N_OUTPUTS 8 + +static const uint32_t output_bank[N_OUTPUTS] = { GPIOC, GPIOA, GPIOA, GPIOA, GPIOD, GPIOD, GPIOD, GPIOD}; +static const uint32_t output[N_OUTPUTS] = { GPIO9, GPIO8, GPIO11, GPIO12, GPIO0, GPIO1, GPIO2, GPIO3}; + +void output_set (unsigned r) +{ + if (r >= N_OUTPUTS) return; + + gpio_set (output_bank[r], output[r]); +} + +void output_clear (unsigned r) +{ + if (r >= N_OUTPUTS) return; + + gpio_clear (output_bank[r], output[r]); +} + +void outputs_set (unsigned v) +{ + unsigned r; + + for (r = 0; r < N_OUTPUTS; r++, v >>= 1) { + if (v & 1) output_set (r); + else output_clear (r); + } +} + +void output_write (unsigned r, unsigned v) +{ + if (v) output_set (r); + else output_clear (r); +} + + + +void outputs_init (void) +{ + unsigned r; + + for (r = 0; r < N_OUTPUTS; ++r) + gpio_set_mode (output_bank[r], GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, output[r]); + +} diff --git a/radiator-plc/stm32/app/pins.h b/radiator-plc/stm32/app/pins.h new file mode 100644 index 0000000..657cbf9 --- /dev/null +++ b/radiator-plc/stm32/app/pins.h @@ -0,0 +1,53 @@ +#ifndef _PINS_H_ +#define _PINS_H_ + +/* st seem to change these with every chip revision */ + +#define MAP_AF(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, a ); \ + } while (0) + +/* STM32F1 doesn't have AF pull up, but also doesn't disconnect af inputs so just use regular pull up */ +#define MAP_AF_PU(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, a); \ + gpio_set( a ## _PORT, a); \ + } while (0) + +#define MAP_AF_OD(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, a ); \ + } while (0) + + +#define MAP_OUTPUT_PP(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, a ); \ + } while (0) + + +#define MAP_OUTPUT_OD(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, a ); \ + } while (0) + +#define MAP_OUTPUT_OD_SLOW(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_2_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, a ); \ + } while (0) + + +/* STM32F1 madly uses the output register to drive the other end of the resistor, so pull up */ +/* requires us to write a 1 there */ + +#define MAP_INPUT_PU(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, a); \ + gpio_set( a ## _PORT, a); \ + } while (0) + + +#define MAP_INPUT(a) do { \ + gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, a); \ + } while (0) + + +#define CLEAR(a) gpio_clear( a ## _PORT, a) +#define SET(a) gpio_set( a ## _PORT, a) +#define GET(a) gpio_get( a ## _PORT, a) + +#endif diff --git a/radiator-plc/stm32/app/project.h b/radiator-plc/stm32/app/project.h new file mode 100644 index 0000000..85f14b9 --- /dev/null +++ b/radiator-plc/stm32/app/project.h @@ -0,0 +1,29 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + + +#include +#include + +#include "pins.h" +#include "ring.h" +#include "1wire.h" + +#include "prototypes.h" + +#define MEH do { printf("Z: %s:%d\r\n",__FUNCTION__,__LINE__); } while (0) + +#define CLOCK_HZ 72000000 + diff --git a/radiator-plc/stm32/app/prototypes.h b/radiator-plc/stm32/app/prototypes.h new file mode 100644 index 0000000..2347f01 --- /dev/null +++ b/radiator-plc/stm32/app/prototypes.h @@ -0,0 +1,80 @@ +/* hexdump.c */ +extern void hexdump(void *_d, int len); +/* main.c */ +extern volatile int request_search; +extern int main(void); +/* ring.c */ +extern void ring_init(ring_t *r, uint8_t *buf, size_t len); +extern int ring_write_byte(ring_t *r, uint8_t c); +extern int ring_read_byte(ring_t *r, uint8_t *c); +extern int ring_write(ring_t *r, uint8_t *buf, size_t len); +extern int ring_empty(ring_t *r); +/* ticker.c */ +extern volatile uint32_t ticks; +extern uint32_t clock_scale; +extern void delay_us(uint32_t d); +extern void sys_tick_handler(void); +extern void delay_ms(uint32_t d); +extern void ticker_init(void); +/* usart.c */ +extern ring_t rx1_ring; +extern ring_t tx1_ring; +extern unsigned locked; +extern void usart_ticker(void); +extern void usart1_isr(void); +extern int _write(int file, char *ptr, int len); +extern void usart1_queue(uint8_t d); +extern void usart1_queue_buf(void *buf, size_t len); +extern void usart1_drain(void); +extern void usart_init(void); +/* watchdog.c */ +extern void watchdog_init(void); +extern void sausages(void); +/* led.c */ +extern void led_init(void); +extern void led1_set(void); +extern void led1_clear(void); +extern void led_tick(void); +/* 1wire.c */ +extern int onewire_reset(void); +extern void onewire_one(void); +extern void onewire_zero(void); +extern int onewire_read(void); +extern void onewire_write_byte(uint8_t v); +extern uint8_t onewire_read_byte(void); +extern void onewire_read_bytes(uint8_t *buf, int n); +extern void onewire_write_bytes(const uint8_t *buf, int n); +extern int onewire_select(const Onewire_addr *a); +extern int onewire_reset_and_select(const Onewire_addr *a); +extern int onewire_wait_complete(unsigned timeout); +extern int onewire_check_crc(uint8_t *buf, int n, uint8_t v); +extern int onewire_search(void); +extern void onewire_init(void); +/* inputs.c */ +extern int input_get(unsigned r); +extern void inputs_init(void); +/* outputs.c */ +extern void output_set(unsigned r); +extern void output_clear(unsigned r); +extern void outputs_set(unsigned v); +extern void output_write(unsigned r, unsigned v); +extern void outputs_init(void); +/* logic.c */ +extern volatile int temp[8]; +extern int low_limit[8]; +extern int high_limit[8]; +extern int valve_state[8]; +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); +/* ds18b20.c */ +extern unsigned extract_leu16(uint8_t *d); +extern int extract_les16(uint8_t *d); +extern int ds18b20_read_sp(const Onewire_addr *a, uint8_t *buf, unsigned len); +extern int ds18b20_write_sp(const Onewire_addr *a, uint8_t *buf, unsigned len); +extern int ds18b20_convert(const Onewire_addr *a, unsigned timeout); +extern int ds18b20_set_12(const Onewire_addr *a); +extern int ds18b20_read(const Onewire_addr *a, int *temp); +extern int ds18b20_set_12_convert(const Onewire_addr *a); +extern int ds18b20_set_12_convert_and_read(const Onewire_addr *a, int *temp); diff --git a/radiator-plc/stm32/app/radiator-plc.ld b/radiator-plc/stm32/app/radiator-plc.ld new file mode 100644 index 0000000..0fb2378 --- /dev/null +++ b/radiator-plc/stm32/app/radiator-plc.ld @@ -0,0 +1,38 @@ +/* + * This file is part of the libopencm3 project. + * + * Copyright (C) 2009 Uwe Hermann + * + * This library is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with this library. If not, see . + */ + +/* Linker script for Olimex STM32-H103 (STM32F103RBT6, 128K flash, 20K RAM). */ + +/* Define memory regions. */ +MEMORY +{ + rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K + ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K +} + +/* Include the common ld script. */ +INCLUDE libopencm3_stm32f1.ld + + + + +/* PROVIDE(_stack = dfu_shared_location ); */ + + + diff --git a/radiator-plc/stm32/app/rain.c b/radiator-plc/stm32/app/rain.c new file mode 100644 index 0000000..4188990 --- /dev/null +++ b/radiator-plc/stm32/app/rain.c @@ -0,0 +1,100 @@ +#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/ring.c b/radiator-plc/stm32/app/ring.c new file mode 100644 index 0000000..c1d1803 --- /dev/null +++ b/radiator-plc/stm32/app/ring.c @@ -0,0 +1,69 @@ +#include "project.h" + + +static inline size_t +ring_next (ring_t *r, size_t p) +{ + p++; + + if (p >= r->size) + p -= r->size; + + return p; +} + +void +ring_init (ring_t *r, uint8_t *buf, size_t len) +{ + r->data = buf; + r->size = len; + r->write = 0; + r->read = 0; +} + +int +ring_write_byte (ring_t *r, uint8_t c) +{ + size_t n = ring_next (r, r->write); + + if (n == r->read) + return -1; + + r->data[r->write] = c; + + r->write = n; + + return 0; +} + + +int +ring_read_byte (ring_t *r, uint8_t *c) +{ + size_t n = ring_next (r, r->read); + + if (r->read == r->write) + return -1; + + *c = r->data[r->read]; + r->read = n; + + return 0; +} + +int +ring_write (ring_t *r, uint8_t *buf, size_t len) +{ + while (len--) { + if (ring_write_byte (r, * (buf++))) + return -1; + } + + return 0; +} + +int +ring_empty (ring_t *r) +{ + return (r->read == r->write) ? 1 : 0; +} diff --git a/radiator-plc/stm32/app/ring.h b/radiator-plc/stm32/app/ring.h new file mode 100644 index 0000000..1eae65b --- /dev/null +++ b/radiator-plc/stm32/app/ring.h @@ -0,0 +1,6 @@ +typedef struct ring { + uint8_t *data; + size_t size; + size_t write; + size_t read; +} ring_t; diff --git a/radiator-plc/stm32/app/ticker.c b/radiator-plc/stm32/app/ticker.c new file mode 100644 index 0000000..bb346fb --- /dev/null +++ b/radiator-plc/stm32/app/ticker.c @@ -0,0 +1,97 @@ +#include "project.h" + +static volatile uint32_t delay_ms_count; +volatile uint32_t ticks; +static volatile int tick_ready; + +uint32_t clock_scale = 72; + +void +delay_us (uint32_t d) +{ + d *= clock_scale; + + while (d--) + __asm__ ("nop"); +} + + + +void +sys_tick_handler (void) +{ + static unsigned slow = 0; + static unsigned medium = 0; + + if (delay_ms_count) + delay_ms_count--; + + ticks++; + + if (!tick_ready) + return; + + //buttons_ticker(); + // + led_tick(); + usart_ticker(); + + + medium++; + + if (medium < 1000) return; + + medium = 0; + + //target_tick(); + + slow++; + + if (slow < 2) return; + logic_tick(); + + +} + + + +void +delay_ms (uint32_t d) +{ + delay_ms_count = d; + + while (delay_ms_count); +} + + +void +ticker_init (void) +{ + uint32_t v, w; + + /*Start periodic timer */ + systick_set_clocksource (STK_CSR_CLKSOURCE_AHB_DIV8); + /* 72MHz / 8 = > 9Mhz */ + systick_set_reload (9000); + /* 9MHz / 9000 => 1kHz */ + systick_interrupt_enable(); + systick_counter_enable(); + + do { + clock_scale--; + v = ticks; + + while (v == ticks); + + delay_us (1000); + w = ticks; + v++; + w -= v; + } while (w); + + + tick_ready = 1; + + SCS_DEMCR |= SCS_DEMCR_TRCENA; + SCS_DWT_CTRL |= SCS_DWT_CTRL_CYCCNTENA; +} diff --git a/radiator-plc/stm32/app/usart.c b/radiator-plc/stm32/app/usart.c new file mode 100644 index 0000000..52866b7 --- /dev/null +++ b/radiator-plc/stm32/app/usart.c @@ -0,0 +1,149 @@ +#include "project.h" + +#define BUFFER_SIZE 768 + +ring_t rx1_ring; +static uint8_t rx1_ring_buf[BUFFER_SIZE]; + +ring_t tx1_ring; +static uint8_t tx1_ring_buf[BUFFER_SIZE]; + +//unsigned locked = 25000; +unsigned locked = 0; + + + + + +void usart_ticker (void) +{ + if (locked) locked--; +} + + + +static char unlock[] = "octopus banana"; +static int unlock_ptr = 0; + + +void +usart1_isr (void) +{ + uint8_t data; + + /* Check if we were called because of RXNE. */ + if (((USART_CR1 (USART1) & USART_CR1_RXNEIE) != 0) && + ((USART_SR (USART1) & USART_SR_RXNE) != 0)) { + + /* Retrieve the data from the peripheral. */ + data = usart_recv (USART1); + + if (locked) { + if (unlock[unlock_ptr] == data) + unlock_ptr++; + else + unlock_ptr = 0; + + if (!unlock[unlock_ptr]) { + printf ("Unlocked!\r\n"); + unlock_ptr = 0; + locked = 0; + } + } + + + if (!locked) + ring_write_byte (&rx1_ring, data); + } + + /* Check if we were called because of TXE. */ + if (((USART_CR1 (USART1) & USART_CR1_TXEIE) != 0) && + ((USART_SR (USART1) & USART_SR_TXE) != 0)) { + + if (ring_read_byte (&tx1_ring, &data)) { + /*No more data, Disable the TXE interrupt, it's no longer needed. */ + USART_CR1 (USART1) &= ~USART_CR1_TXEIE; + } else + usart_send (USART1, data); + } + +} + + +int +_write (int file, char *ptr, int len) +{ + int ret = len; + + if (file == 1) { + while (len--) { + if (*ptr == '\n') + ring_write_byte (&tx1_ring, '\r'); + + ring_write_byte (&tx1_ring, * (ptr++)); + } + + USART_CR1 (USART1) |= USART_CR1_TXEIE; + return ret; + } + + errno = EIO; + return -1; +} + +void +usart1_queue (uint8_t d) +{ + ring_write_byte (&tx1_ring, d); + USART_CR1 (USART1) |= USART_CR1_TXEIE; +} +void +usart1_queue_buf (void *buf, size_t len) +{ + ring_write (&tx1_ring, buf, len); + USART_CR1 (USART1) |= USART_CR1_TXEIE; +} + + + +void +usart1_drain (void) +{ + while (!ring_empty (&tx1_ring)); +} + + + +void +usart_init (void) +{ + rcc_periph_clock_enable (RCC_USART1); + + ring_init (&rx1_ring, rx1_ring_buf, sizeof (rx1_ring_buf)); + ring_init (&tx1_ring, tx1_ring_buf, sizeof (tx1_ring_buf)); + + + /* Enable the USART1,2 interrupt. */ + nvic_enable_irq (NVIC_USART1_IRQ); + + /* Map pins, and set usart2 to have pull ups */ + gpio_set_mode (GPIOA, GPIO_MODE_OUTPUT_50_MHZ, + GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, GPIO_USART1_TX); + gpio_set_mode (GPIOA, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, + GPIO_USART1_RX); + + /* Setup UART1 parameters. */ + usart_set_baudrate (USART1, 38400); + usart_set_databits (USART1, 8); + usart_set_stopbits (USART1, USART_STOPBITS_1); + usart_set_parity (USART1, USART_PARITY_NONE); + usart_set_flow_control (USART1, USART_FLOWCONTROL_NONE); + usart_set_mode (USART1, USART_MODE_TX_RX); + + + /* Enable USART1 Receive interrupt. */ + USART_CR1 (USART1) |= USART_CR1_RXNEIE; + + /* Finally enable the USARTs. */ + usart_enable (USART1); +} diff --git a/radiator-plc/stm32/app/watchdog.c b/radiator-plc/stm32/app/watchdog.c new file mode 100644 index 0000000..8516119 --- /dev/null +++ b/radiator-plc/stm32/app/watchdog.c @@ -0,0 +1,16 @@ +#include "project.h" + + + +void +watchdog_init (void) +{ + //iwdg_set_period_ms (10000); + //iwdg_start(); +} + +void +sausages() +{ + iwdg_reset(); +} diff --git a/radiator-plc/stm32/libopencm3 b/radiator-plc/stm32/libopencm3 new file mode 160000 index 0000000..5c73d60 --- /dev/null +++ b/radiator-plc/stm32/libopencm3 @@ -0,0 +1 @@ +Subproject commit 5c73d601763dd140bff020be8d6d06f03c2bea78 diff --git a/run_ot_monitor b/run_ot_monitor new file mode 100755 index 0000000..3916435 --- /dev/null +++ b/run_ot_monitor @@ -0,0 +1,4 @@ +#!/bin/bash + +wish otmonitor/otmonitor.vfs/main.tcl -f ./.otmonitor.config + diff --git a/set_radiator_target b/set_radiator_target new file mode 100755 index 0000000..85ef1f9 --- /dev/null +++ b/set_radiator_target @@ -0,0 +1,10 @@ +#!/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/stm32/.gitignore b/stm32/.gitignore deleted file mode 100644 index 08e4672..0000000 --- a/stm32/.gitignore +++ /dev/null @@ -1,16 +0,0 @@ -.gitmodules -*.o -*.d -*.map -*.hex -*.elf -*.swp -*~ -*.dfu -plot/data -plot/script -*.bin -scmversion -commit.h -opencm3.build.stamp -*.orig diff --git a/stm32/Makefile b/stm32/Makefile deleted file mode 100644 index e1cd497..0000000 --- a/stm32/Makefile +++ /dev/null @@ -1,8 +0,0 @@ - -default: - make -C libopencm3 - make -C app - -clean: - make -C libopencm3 clean - make -C app clean diff --git a/stm32/Makefile.include b/stm32/Makefile.include deleted file mode 100644 index 4f5cbd9..0000000 --- a/stm32/Makefile.include +++ /dev/null @@ -1,44 +0,0 @@ -## -## This file is part of the libopencm3 project. -## -## Copyright (C) 2009 Uwe Hermann -## Copyright (C) 2010 Piotr Esden-Tempski -## -## This library is free software: you can redistribute it and/or modify -## it under the terms of the GNU Lesser General Public License as published by -## the Free Software Foundation, either version 3 of the License, or -## (at your option) any later version. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU Lesser General Public License for more details. -## -## You should have received a copy of the GNU Lesser General Public License -## along with this library. If not, see . -## - -LIBNAME = opencm3_stm32f1 -DEFS = -DSTM32F1 - -FP_FLAGS ?= -msoft-float -ARCH_FLAGS = -mthumb -mcpu=cortex-m3 $(FP_FLAGS) -mfix-cortex-m3-ldrd - -################################################################################ -# OpenOCD specific variables - -OOCD ?= openocd -OOCD_INTERFACE ?= interface/stlink-v2.cfg -OOCD_BOARD ?= target/stm32f1x.cfg - -################################################################################ -# Black Magic Probe specific variables -# Set the BMP_PORT to a serial port and then BMP is used for flashing -BMP_PORT ?= - -################################################################################ -# texane/stlink specific variables -#STLINK_PORT ?= :4242 - - -include ../Makefile.rules diff --git a/stm32/Makefile.rules b/stm32/Makefile.rules deleted file mode 100644 index a723e6f..0000000 --- a/stm32/Makefile.rules +++ /dev/null @@ -1,261 +0,0 @@ -# -## This file is part of the libopencm3 project. -## -## Copyright (C) 2009 Uwe Hermann -## Copyright (C) 2010 Piotr Esden-Tempski -## Copyright (C) 2013 Frantisek Burian -## -## This library is free software: you can redistribute it and/or modify -## it under the terms of the GNU Lesser General Public License as published by -## the Free Software Foundation, either version 3 of the License, or -## (at your option) any later version. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU Lesser General Public License for more details. -## -## You should have received a copy of the GNU Lesser General Public License -## along with this library. If not, see . -## - -# Be silent per default, but 'make V=1' will show all compiler calls. -ifneq ($(V),1) -Q := @ -NULL := 2>/dev/null -endif - -############################################################################### -# Executables - -PREFIX ?= arm-none-eabi - -CC := $(PREFIX)-gcc -CXX := $(PREFIX)-g++ -LD := $(PREFIX)-gcc -AR := $(PREFIX)-ar -AS := $(PREFIX)-as -OBJCOPY := $(PREFIX)-objcopy -OBJDUMP := $(PREFIX)-objdump -GDB := $(PREFIX)-gdb -STFLASH = $(shell which st-flash) -STYLECHECK := /checkpatch.pl -STYLECHECKFLAGS := --no-tree -f --terse --mailback -STYLECHECKFILES := $(shell find . -name '*.[ch]') - - -############################################################################### -# Source files - -LDSCRIPT ?= $(BINARY).ld - -#OBJS += $(BINARY).o - - -ifeq ($(strip $(OPENCM3_DIR)),) -# user has not specified the library path, so we try to detect it - -# where we search for the library -LIBPATHS := ./libopencm3 ../libopencm3 - -OPENCM3_DIR := $(wildcard $(LIBPATHS:=/locm3.sublime-project)) -OPENCM3_DIR := $(firstword $(dir $(OPENCM3_DIR))) - -ifeq ($(strip $(OPENCM3_DIR)),) -$(warning Cannot find libopencm3 library in the standard search paths.) -$(error Please specify it through OPENCM3_DIR variable!) -endif -endif - -ifeq ($(V),1) -$(info Using $(OPENCM3_DIR) path to library) -endif - -INCLUDE_DIR = $(OPENCM3_DIR)/include -LIB_DIR = $(OPENCM3_DIR)/lib -SCRIPT_DIR = $(OPENCM3_DIR)/scripts - -############################################################################### -# C flags - -CFLAGS += -Os -g -CFLAGS += -Wextra -Wimplicit-function-declaration -CFLAGS += -Wmissing-prototypes -Wstrict-prototypes -CFLAGS += -fno-common -ffunction-sections -fdata-sections - -############################################################################### -# C++ flags - -CXXFLAGS += -Os -g -CXXFLAGS += -Wextra -Wshadow -Wredundant-decls -Weffc++ -CXXFLAGS += -fno-common -ffunction-sections -fdata-sections - -############################################################################### -# C & C++ preprocessor common flags - -CPPFLAGS += -MD -CPPFLAGS += -Wall -Wundef - -INCLUDES = -I$(INCLUDE_DIR) -DEFINES = $(DEFS) - -CPPFLAGS += $(INCLUDES) $(DEFINES) - -############################################################################### -# Linker flags - -LDFLAGS += --static -nostartfiles -LDFLAGS += -L$(LIB_DIR) -LDFLAGS += -T$(LDSCRIPT) -LDFLAGS += -Wl,-Map=$(*).map -LDFLAGS += -Wl,--gc-sections -ifeq ($(V),99) -LDFLAGS += -Wl,--print-gc-sections -endif - -############################################################################### -# Used libraries - -LDLIBS += -l$(LIBNAME) -LDLIBS += -Wl,--start-group -lc -lgcc -lnosys -Wl,--end-group - -############################################################################### -############################################################################### -############################################################################### - -.SUFFIXES: .elf .bin .hex .srec .list .map .images .dfu -.SECONDEXPANSION: -.SECONDARY: - -all: elf - - -elf: $(BINARY).elf -bin: $(BINARY).bin -hex: $(BINARY).hex -srec: $(BINARY).srec -list: $(BINARY).list - -images: $(BINARY).images -flash: $(BINARY).flash - -%.images: %.bin %.hex %.srec %.list %.map %.dfu - @#printf "*** $* images generated ***\n" - -%.bin: %.elf - @#printf " OBJCOPY $(*).bin\n" - $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).bin - -%.hex: %.elf - @#printf " OBJCOPY $(*).hex\n" - $(Q)$(OBJCOPY) -Oihex $(*).elf $(*).hex - -%.dfu: %.elf - @#printf " OBJCOPY $(*).dfu\n" - $(Q)$(OBJCOPY) -Obinary $(*).elf $(*).dfu - -%.srec: %.elf - @#printf " OBJCOPY $(*).srec\n" - $(Q)$(OBJCOPY) -Osrec $(*).elf $(*).srec - -%.list: %.elf - @#printf " OBJDUMP $(*).list\n" - $(Q)$(OBJDUMP) -S $(*).elf > $(*).list - -fish: - echo %.elf %.map: $(OBJS) $(LDSCRIPT) $(LIB_DIR)/lib$(LIBNAME).a - echo $(BINARY).elf - -%.elf %.map: $(OBJS) $(LDSCRIPT) $(LIB_DIR)/lib$(LIBNAME).a - @#printf " LD $(*).elf\n" - $(Q)$(LD) $(LDFLAGS) $(ARCH_FLAGS) $(OBJS) $(LDLIBS) -o $(*).elf - -%.o: %.c $(OPENCM3_DIR)/../opencm3.build.stamp - @#printf " CC $(*).c\n" - $(Q)$(CC) $(CFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $(*).o -c $(*).c - -%.o: %.cxx $(OPENCM3_DIR)/../opencm3.build.stamp - @#printf " CXX $(*).cxx\n" - $(Q)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $(*).o -c $(*).cxx - -%.o: %.cpp $(OPENCM3_DIR)/../opencm3.build.stamp - @#printf " CXX $(*).cpp\n" - $(Q)$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(ARCH_FLAGS) -o $(*).o -c $(*).cpp - -clean: - @#printf " CLEAN\n" - $(Q)$(RM) *.o *.d *.elf *.bin *.hex *.srec *.list *.map *~ *.dfu ${EXTRACLEANS} - -stylecheck: $(STYLECHECKFILES:=.stylecheck) -styleclean: $(STYLECHECKFILES:=.styleclean) - -# the cat is due to multithreaded nature - we like to have consistent chunks of text on the output -%.stylecheck: % - $(Q)$(SCRIPT_DIR)$(STYLECHECK) $(STYLECHECKFLAGS) $* > $*.stylecheck; \ - if [ -s $*.stylecheck ]; then \ - cat $*.stylecheck; \ - else \ - rm -f $*.stylecheck; \ - fi; - -%.styleclean: - $(Q)rm -f $*.stylecheck; - - -%.stlink-flash: %.bin - @printf " FLASH $<\n" - $(Q)$(STFLASH) write $(*).bin 0x8000000 - -ifeq ($(STLINK_PORT),) -ifeq ($(BMP_PORT),) -ifeq ($(OOCD_SERIAL),) -%.flash: %.hex - @printf " FLASH $<\n" - @# IMPORTANT: Don't use "resume", only "reset" will work correctly! - $(Q)$(OOCD) -f $(OOCD_INTERFACE) \ - -f $(OOCD_BOARD) \ - -c "init" -c "reset init" \ - -c "flash write_image erase $(*).hex" \ - -c "reset" \ - -c "shutdown" $(NULL) -else -%.flash: %.hex - @printf " FLASH $<\n" - @# IMPORTANT: Don't use "resume", only "reset" will work correctly! - $(Q)$(OOCD) -f $(OOCD_INTERFACE) \ - -f $(OOCD_BOARD) \ - -c "ft2232_serial $(OOCD_SERIAL)" \ - -c "init" -c "reset init" \ - -c "flash write_image erase $(*).hex" \ - -c "reset" \ - -c "shutdown" $(NULL) -endif -else -%.flash: %.elf - @printf " GDB $(*).elf (flash)\n" - $(Q)$(GDB) --batch \ - -ex 'target extended-remote $(BMP_PORT)' \ - -x $(SCRIPT_DIR)/black_magic_probe_flash.scr \ - $(*).elf -endif -else -%.flash: %.elf - @printf " GDB $(*).elf (flash)\n" - $(Q)$(GDB) --batch \ - -ex 'target extended-remote $(STLINK_PORT)' \ - -x $(SCRIPT_DIR)/stlink_flash.scr \ - $(*).elf -endif - -.PHONY: images clean stylecheck styleclean elf bin hex srec list - --include $(OBJS:.o=.d) - - -$(LIB_DIR)/lib$(LIBNAME).a: $(OPENCM3_DIR)/../opencm3.build.stamp - - -$(OPENCM3_DIR)/../opencm3.build.stamp: - ${MAKE} -C ${OPENCM3_DIR} - touch $@ - diff --git a/stm32/app/1wire.c b/stm32/app/1wire.c deleted file mode 100644 index c374c5c..0000000 --- a/stm32/app/1wire.c +++ /dev/null @@ -1,392 +0,0 @@ -#include "project.h" - -#define USART2_TX GPIO_USART2_TX -#define USART2_TX_PORT GPIOA - -#define BIT_ZERO '0' -#define BIT_ONE '1' -#define BIT_UNKNOWN 'U' -#define BIT_CONFLICT 'C' - - -static int poke; -static volatile unsigned exchange_timeout; - -void onewire_tick (void) -{ - static unsigned ticker; - - if (exchange_timeout) - exchange_timeout--; - - - ticker++; - - if (ticker < MS_TO_TICKS (5000)) - return; - - ticker = 0; - poke = 1; - - -} - - -static unsigned usart_send_ready (uint32_t usart) -{ - return (USART_SR (usart) & USART_SR_TXE); -} - - -static unsigned usart_recv_ready (uint32_t usart) -{ - return (USART_SR (usart) & USART_SR_RXNE); -} - - - -static int onewire_exchange (int tx, unsigned timeout) -{ - int rx; - (void) USART_SR (USART2); - (void) usart_recv (USART2); - - - exchange_timeout = MS_TO_TICKS (timeout); - - while (!usart_send_ready (USART2)) - if (!exchange_timeout) - return -1; - - usart_send (USART2, tx); - - exchange_timeout = MS_TO_TICKS (timeout); - - while (!usart_recv_ready (USART2)) - if (!exchange_timeout) - return -1; - - - rx = usart_recv (USART2); - - return rx; - -} - -/* returns 1 if nothing on bus or error */ -int -onewire_reset (void) -{ - int ret; - - usart_set_baudrate (USART2, 9600); - delay_ms (1); - ret = onewire_exchange (0xf0, 60); - usart_set_baudrate (USART2, 115200); - delay_ms (1); - - return (ret == 0xf0); -} - -static void -onewire_one (void) -{ - onewire_exchange (0xff, 3); -} - -static void -onewire_zero (void) -{ - onewire_exchange (0x00, 3); -} - -static int -onewire_read (void) -{ - uint8_t rx; - - rx = onewire_exchange (0xff, 3); - - return (rx == 0xff); -} - - -void -onewire_write_byte (uint8_t v) -{ - int c; - - - for (c = 0; c < 8; ++c) { - if (v & 1) - onewire_one(); - else - onewire_zero(); - - v >>= 1; - } -} - -uint8_t -onewire_read_byte (void) -{ - uint8_t v = 0; - int c; - - - for (c = 0; c < 8; ++c) { - v >>= 1; - - if (onewire_read()) - v |= 0x80; - } - - - return v; -} - - - -void -onewire_read_bytes (uint8_t *buf, int n) -{ - while (n--) - * (buf++) = onewire_read_byte(); -} - - -void -onewire_write_bytes (const uint8_t *buf, int n) -{ - while (n--) - onewire_write_byte (* (buf++)); -} - - -int onewire_select (const Onewire_addr *a) -{ - if (!a) - onewire_write_byte (ONEWIRE_SKIP_ROM); - else { - onewire_write_byte (ONEWIRE_MATCH_ROM); - onewire_write_bytes (a->a, 8); - } - - return 0; -} - - -int onewire_reset_and_select (const Onewire_addr *a) -{ - if (onewire_reset()) - return 1; - - if (onewire_select (a)) - return 1; - - return 0; -} - - - -int onewire_wait_complete (unsigned timeout) -{ - while (! (onewire_read())) { - delay_ms (10); - - if (! (timeout--)) - return 1; - } - - return 0; -} - - - -int -onewire_check_crc (uint8_t *buf, int n, uint8_t v) -{ - uint8_t crc = 0; - int i; - - while (n--) { - uint8_t v = * (buf++); - - for (i = 0; i < 8; ++i) { - uint8_t mix = (crc ^ v) & 0x01; - crc >>= 1; - - if (mix) - crc ^= 0x8C; - - v >>= 1; - } - } - - return ! (crc == v); - -} - -static int onewire_conduct_search (uint8_t *bits) -{ - unsigned i, ir, r; - - if (onewire_reset()) - return -1; - - onewire_write_byte (ONEWIRE_SEARCH_ROM); - - - for (i = 0; i < 64; ++i) { - - r = onewire_read(); - ir = onewire_read(); - -#if 0 - - if ((i == 27) || (i == 24) || (i == 39)) - ir = r = 0; - -#endif - - switch (bits[i]) { - case BIT_UNKNOWN: - - if (!r && ir) { /* Zero */ - bits[i] = BIT_ZERO; - onewire_zero(); - } else if (r && !ir) { /*One */ - bits[i] = BIT_ONE; - onewire_one(); - } else if (r && ir) { /*Nothing here */ - //MEH; - return -1; - } else if (!r && !ir) { /*Both */ - bits[i] = BIT_CONFLICT; - onewire_zero(); - } - - break; - - case BIT_CONFLICT: - if (!r && !ir) /*Both */ - onewire_zero(); - else { - //MEH; - return -1; - } - - break; - - case BIT_ZERO: - if (!r) - onewire_zero(); - else { - //MEH; - return -1; - } - - break; - - case BIT_ONE: - if (!ir) - onewire_one(); - else { - //MEH; - return -1; - } - - break; - } - } - - return 0; -} - -static int onewire_next (uint8_t *bits) -{ - unsigned i; - - for (i = 0; i < 64; ++i) { - - if (bits[63 - i] == BIT_CONFLICT) { - bits[63 - i] = BIT_ONE; - return 1; - } - - bits[63 - i] = BIT_UNKNOWN; - } - - return 0; -} - -static void onewire_bits_to_a (uint8_t *bits, Onewire_addr *a) -{ - unsigned i, j, c; - - for (i = 0, j = 0; i < 8; ++i) { - - a->a[i] = 0; - - for (c = 1; c < 0x100; c <<= 1, ++j) { - if (bits[j] == BIT_ONE) - a->a[i] |= c; - } - } -} - -int onewire_search (void) -{ - uint8_t bits[64]; - Onewire_addr a; - int r, c; - - delay_ms (2000); - - memset (bits, BIT_UNKNOWN, sizeof (bits)); - - do { - r = onewire_conduct_search (bits); - - if (!r) { - onewire_bits_to_a (bits, &a); - c = onewire_check_crc (&a.a[0], 7, a.a[7]); - - if (!c) { - printf ("QO: {0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x,0x%02x}\r\n", - a.a[0], - a.a[1], - a.a[2], - a.a[3], - a.a[4], - a.a[5], - a.a[6], - a.a[7]); - } - - } - - } while (onewire_next (bits)); - - - return 0; -} - - - -void -onewire_init (void) -{ - MAP_AF_OD (USART2_TX); - - usart_set_baudrate (USART2, 115200); - usart_set_databits (USART2, 8); - usart_set_stopbits (USART2, USART_STOPBITS_1); - usart_set_parity (USART2, USART_PARITY_NONE); - usart_set_flow_control (USART2, USART_FLOWCONTROL_NONE); - usart_set_mode (USART2, USART_MODE_TX_RX); - - USART_CR3 (USART2) |= USART_CR3_HDSEL; - - usart_enable (USART2); -} diff --git a/stm32/app/1wire.h b/stm32/app/1wire.h deleted file mode 100644 index 4a31418..0000000 --- a/stm32/app/1wire.h +++ /dev/null @@ -1,10 +0,0 @@ -#define ONEWIRE_SKIP_ROM 0xcc -#define ONEWIRE_SEARCH_ROM 0xf0 -#define ONEWIRE_MATCH_ROM 0x55 - -#define ONEWIRE_ADDR_LEN 8 -typedef struct { - uint8_t a[ONEWIRE_ADDR_LEN]; -} Onewire_addr; - - diff --git a/stm32/app/Makefile b/stm32/app/Makefile deleted file mode 100644 index d4a4f5b..0000000 --- a/stm32/app/Makefile +++ /dev/null @@ -1,132 +0,0 @@ -## -## This file is part of the libopencm3 project. -## -## Copyright (C) 2009 Uwe Hermann -## -## This library is free software: you can redistribute it and/or modify -## it under the terms of the GNU Lesser General Public License as published by -## the Free Software Foundation, either version 3 of the License, or -## (at your option) any later version. -## -## This library is distributed in the hope that it will be useful, -## but WITHOUT ANY WARRANTY; without even the implied warranty of -## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -## GNU Lesser General Public License for more details. -## -## You should have received a copy of the GNU Lesser General Public License -## along with this library. If not, see . -## - -CPROTO=cproto -PROG=boiler - -CSRCS=main.c ot.c ot_phy_rx.c ot_phy_tx.c led.c ticker.c usart.c ring.c stdio.c util.c commit.c cmd.c pic.c 1wire.c temp.c ds1820.c pressure.c adc.c -HSRCS=pins.h project.h ring.h - -EXTRACLEANS=*.orig scmversion commit.h - - - - -TARGET=10.32.96.18 -#TARGET2=10.32.96.107 - -#soup: ${PROG}.hex -# rsync -vaP ${PROG}.hex ${TARGET2}:/tmp/boiler.hex -# RV=$$(arm-none-eabi-objdump --disassemble=reset_handler ${PROG}.elf | awk '/^0/ { print "0x"$$1 }') && \ -# ssh ${TARGET2} "$(OOCD) -f $(OOCD_INTERFACE) \ -# -f $(OOCD_BOARD) \ -# -c init -c \"reset init\" \ -# -c \"flash write_image erase /tmp/boiler.hex\" \ -# -c \"reset halt\" \ -# -c \"resume $${RV}\" \ -# -c shutdown" $(NULL) && \ -# echo reset_handler is at $${RV} -# - - - -V=1 -default: ${PROG}.fw.bin - rsync -vaP ${PROG}.fw.bin ${TARGET}:/tmp/boiler.fw - ssh ${TARGET} /etc/stm32/startup /tmp/boiler.fw - - -install: ${PROG}.fw.bin - rsync -vaP ${PROG}.fw.bin ${TARGET}:/etc/stm32/boiler.fw - - -128k_of_ff.bin: - dd if=/dev/zero bs=128k count=1 | tr '\0' '\377' > $@ - - -${PROG}.fw.bin:${PROG}.bin 128k_of_ff.bin - cat ${PROG}.bin 128k_of_ff.bin | dd of=$@ bs=128k iflag=fullblock count=1 - - -program: ${PROG}.elf - echo halt | nc -t localhost 4444 - echo flash write_image erase ${PWD}/$< 0x2000 | nc -t localhost 4444 - echo reset halt | nc -t localhost 4444 - RV=$$(arm-none-eabi-objdump --disassemble=reset_handler $< | awk '/^0/ { print "0x"$$1 }') && \ - echo resume $${RV} | nc -t localhost 4444 && \ - echo reset vector at $${RV} - -run:${PROG}.run - -%.run: %.hex - RV=$$(arm-none-eabi-objdump --disassemble=reset_handler $(*).elf | awk '/^0/ { print "0x"$$1 }') && \ - $(Q)$(OOCD) -f $(OOCD_INTERFACE) \ - -f $(OOCD_BOARD) \ - -c "init" -c "reset init" \ - -c "flash write_image erase $(*).hex" \ - -c "reset halt" \ - -c "resume $${RV}" \ - -c "shutdown" $(NULL) && \ - echo reset_handler is at $${RV} - - - - -BINARY = ${PROG} -OBJS = ${CSRCS:%.c=%.o} - -include ../Makefile.include - -CFLAGS+=-Wno-redundant-decls -Wno-unused-parameter -DDEBUG - -INCLUDES += -I.. - -protos: - echo -n > prototypes.h - ${CPROTO} $(INCLUDES) $(DEFINES) -e -v ${CSRCS} > prototypes.h.tmp - mv -f prototypes.h.tmp prototypes.h - -server: - $(OOCD) -f $(OOCD_INTERFACE) \ - -f $(OOCD_BOARD) - -debug: ${PROG}.elf - ${PREFIX}-gdb -x gdb.script ${PROG}.elf - -tidy: - astyle -A3 -s2 --attach-extern-c -L -c -w -Y -m0 -f -p -H -U -k3 -xj -xd ${CSRCS} ${HSRCS} - -HEAD_REF = $(shell git rev-parse --verify --short HEAD) -SCM_DIRTY = $(shell if ! git diff-index --quiet HEAD --; then echo "+dirty"; fi) -SCMVERSION = ${HEAD_REF}${SCM_DIRTY} - -scmversion: - echo -n ${SCMVERSION} > $@ - -.PHONY: scmversion - -commit.o: commit.h - -commit.h: scmversion - echo -n '#define COMMIT_ID "' > $@ - echo -n `cat scmversion` >> $@ - echo '"' >> $@ - - - diff --git a/stm32/app/adc.c b/stm32/app/adc.c deleted file mode 100644 index ee6a4f3..0000000 --- a/stm32/app/adc.c +++ /dev/null @@ -1,121 +0,0 @@ -#include "project.h" - -#define T do { printf("%s:%d\r\n",__FILE__,__LINE__); } while (0) - -void adc_dump (void) -{ - printf ("ADC_SR %x ADC_CR1 %x ADC_CR2 %x\r\n", (unsigned) ADC_SR (ADC1), - (unsigned) ADC_CR1 (ADC1), - (unsigned) ADC_CR2 (ADC1)); - -} - - -volatile unsigned timeout; - - -void adc_tick (void) -{ - if (timeout) timeout--; -} - - -static int adc_wait (volatile uint32_t *reg, uint32_t wait_while_set, uint32_t wait_while_clear, unsigned ms) -{ - timeout = MS_TO_TICKS (ms); - - - while ((*reg) & wait_while_set) - if (!timeout) { - printf ("QADC timeout\r\n"); - return -1; - } - - - while ((~ (*reg)) & wait_while_clear) - if (!timeout) { - printf ("QADC timeout\r\n"); - return -1; - } - - - return 0; -} - - - -int adc_calibrate (void) -{ - adc_off (ADC1); - adc_power_on (ADC1); - delay_ms (5); - - ADC_CR2 (ADC1) |= ADC_CR2_RSTCAL; - - if (adc_wait (&ADC_CR2 (ADC1), ADC_CR2_CAL, 0, 2)) return -1; - - ADC_CR2 (ADC1) |= ADC_CR2_CAL; - - if (adc_wait (&ADC_CR2 (ADC1), ADC_CR2_CAL, 0, 2)) return -1; - - return 0; -} - - - - -unsigned adc_convert (unsigned channel) -{ - uint8_t ch = channel; - - adc_calibrate(); - - adc_set_regular_sequence (ADC1, 1, &ch); - - /* Start conversion on regular channels. */ - ADC_CR2 (ADC1) |= ADC_CR2_SWSTART; - - if (adc_wait (&ADC_CR2 (ADC1), ADC_CR2_SWSTART, 0, 2)) - return 0; - - if (adc_wait (&ADC_SR (ADC1), 0, ADC_SR_EOC, 2)) - return 0; - - return adc_read_regular (ADC1); -} - -void adc_init (void) -{ - /* main set ADC clock is 9Mhz */ - - adc_dump(); - - adc_off (ADC1); - rcc_periph_clock_enable (RCC_ADC1); -#if 0 - rcc_peripheral_reset (&RCC_APB2RSTR, RCC_APB2RSTR_ADC1RST); - rcc_peripheral_clear_reset (&RCC_APB2RSTR, RCC_APB2RSTR_ADC1RST); -#endif - - adc_set_dual_mode (ADC_CR1_DUALMOD_IND); - adc_disable_scan_mode (ADC1); - adc_enable_temperature_sensor (ADC1); - adc_set_single_conversion_mode (ADC1); - - adc_set_sample_time (ADC1, ADC_CHANNEL0, ADC_SMPR_SMP_239DOT5CYC); - adc_set_sample_time (ADC1, ADC_CHANNEL17, ADC_SMPR_SMP_239DOT5CYC); /*Want 17.1uS , which is 154 cycles */ - adc_enable_external_trigger_regular (ADC1, ADC_CR2_EXTSEL_SWSTART); - - adc_set_right_aligned (ADC1); - - - adc_dump(); - - adc_calibrate(); - - adc_dump(); - - - -} - diff --git a/stm32/app/boiler.ld b/stm32/app/boiler.ld deleted file mode 100644 index e15beca..0000000 --- a/stm32/app/boiler.ld +++ /dev/null @@ -1,31 +0,0 @@ -/* - * This file is part of the libopencm3 project. - * - * Copyright (C) 2009 Uwe Hermann - * - * This library is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with this library. If not, see . - */ - -/* Linker script for Olimex STM32-H103 (STM32F103RBT6, 128K flash, 20K RAM). */ - -/* Define memory regions. */ -MEMORY -{ - rom (rx) : ORIGIN = 0x08000000, LENGTH = 128K - ram (rwx) : ORIGIN = 0x20000000, LENGTH = 20K -} - -/* Include the common ld script. */ -INCLUDE libopencm3_stm32f1.ld - diff --git a/stm32/app/cmd.c b/stm32/app/cmd.c deleted file mode 100644 index 7e1975a..0000000 --- a/stm32/app/cmd.c +++ /dev/null @@ -1,55 +0,0 @@ -#include "project.h" - - -static void cmd_dispatch (char *cmd) -{ - - printf ("Q received cmd %s\r\n", cmd); - - if (!strncmp (cmd, "CH=", 3)) { - ot_override_ch = atoi (cmd + 3); - printf ("Q CH override set to %d\r\n", ot_override_ch); - } - - - if (!strncmp (cmd, "DHW=", 4)) { - ot_override_dhw = atoi (cmd + 4); - printf ("Q DHW override set to %d\r\n", ot_override_dhw); - } - - if (!strcmp (cmd, "PIC")) { - ot_override_dhw = atoi (cmd + 4); - printf ("Q Entering PIC mode, reset to leave\r\n"); - pic_passthru(); - } - -} - - -void cmd_usart_dispatch (void) -{ - static char cmd[16]; - static unsigned cmd_ptr; - - uint8_t c; - - if (ring_read_byte (&rx1_ring, &c)) - return; - - - if ((c == '\n') || (c == '\r')) { - if (cmd_ptr) - cmd_dispatch (cmd); - - cmd_ptr = 0; - return; - } - - - if (cmd_ptr < (sizeof (cmd) - 1)) { - cmd[cmd_ptr++] = c; - cmd[cmd_ptr] = 0; - } -} - - diff --git a/stm32/app/commit.c b/stm32/app/commit.c deleted file mode 100644 index 207448f..0000000 --- a/stm32/app/commit.c +++ /dev/null @@ -1,3 +0,0 @@ -#include "commit.h" - -char scm_version[] = COMMIT_ID; diff --git a/stm32/app/ds1820.c b/stm32/app/ds1820.c deleted file mode 100644 index 200f4b7..0000000 --- a/stm32/app/ds1820.c +++ /dev/null @@ -1,93 +0,0 @@ -#include "project.h" - -#define DS1820_READ_SCRATCHPAD 0xbe -#define DS1820_CONVERT_T 0x44 - - -#define TIMEOUT 150 - - - -static unsigned extract_leu16 (uint8_t *d) -{ - uint32_t u; - - u = (uint32_t) d[0] | (((uint32_t) d[1]) << 8); - return u; -} - - - -static int extract_les16 (uint8_t *d) -{ - uint32_t u; - u = extract_leu16 (d); - - if (u & 0x8000) u |= 0xffff0000; - - return (int) u; -} - - - - - -static int -ds1820_read_sp (const Onewire_addr *a, unsigned page, uint8_t *buf, unsigned len) -{ - if (onewire_reset_and_select (a)) - return ~0U; - - onewire_write_byte (DS1820_READ_SCRATCHPAD); - onewire_read_bytes (buf, len); - - if ((len == 9) && onewire_check_crc (buf, 8, buf[8])) - return ~0U; - - return 0; -} - - - -static int ds1820_convert_t (const Onewire_addr *a) -{ - - if (onewire_reset_and_select (a)) - return ~0U; - - onewire_write_byte (DS1820_CONVERT_T); - - if (onewire_wait_complete (TIMEOUT)) - return ~0U; - - return 0; -} - -int -ds1820_read (const Onewire_addr *a, int *temp) -{ - uint8_t buf[9]; - int t; - - if (ds1820_read_sp (a, 0, buf, 9)) - return 1; - - if (ds1820_convert_t (a)) - return 1; - - if (ds1820_read_sp (a, 0, buf, 9)) - return 1; - - t = extract_les16 (&buf[0]); - - t <<= 4 ; - - if (temp) - *temp = t; - - - return 0; -} - - - diff --git a/stm32/app/gdb.script b/stm32/app/gdb.script deleted file mode 100644 index 7cf9d09..0000000 --- a/stm32/app/gdb.script +++ /dev/null @@ -1,2 +0,0 @@ -target remote localhost:3333 -cont diff --git a/stm32/app/led.c b/stm32/app/led.c deleted file mode 100644 index bdfd084..0000000 --- a/stm32/app/led.c +++ /dev/null @@ -1,101 +0,0 @@ -#include "project.h" - -#define LED_BOARD GPIO13 -#define LED_BOARD_PORT GPIOC - -#define LED_YELLOW GPIO11 -#define LED_YELLOW_PORT GPIOA - -#define LED_GREEN1 GPIO12 -#define LED_GREEN1_PORT GPIOA - -#define LED_GREEN2 GPIO15 -#define LED_GREEN2_PORT GPIOA - -#define LED_RED GPIO3 -#define LED_RED_PORT GPIOB - -static unsigned led, yellow; - - -void led_red_set (int i) -{ - if (i) - CLEAR (LED_RED); - else - SET (LED_RED); -} - -void led_green1_set (int i) -{ - if (i) - CLEAR (LED_GREEN1); - else - SET (LED_GREEN1); -} - -void led_green2_set (int i) -{ - if (i) - CLEAR (LED_GREEN2); - else - SET (LED_GREEN2); -} - -static void _led_yellow_set (int i) -{ - if (i) - CLEAR (LED_YELLOW); - else - SET (LED_YELLOW); -} - -static void led_board_set (int i) -{ - if (i) - CLEAR (LED_BOARD); - else - SET (LED_BOARD); -} - -void led_blink (void) -{ - led = MS_TO_TICKS (25); -} - - -void led_yellow_set (int i) -{ - yellow = !!i; -} - - -void -led_tick (void) -{ - if (led) { - led--; - - led_board_set (1); - _led_yellow_set (!yellow); - } else { - - led_board_set (0); - _led_yellow_set (yellow); - } -} - - -void -led_init (void) -{ - MAP_OUTPUT_PP (LED_BOARD); - MAP_OUTPUT_PP (LED_RED); - MAP_OUTPUT_PP (LED_GREEN1); - MAP_OUTPUT_PP (LED_GREEN2); - MAP_OUTPUT_PP (LED_YELLOW); - - CLEAR (LED_BOARD); -} - - diff --git a/stm32/app/main.c b/stm32/app/main.c deleted file mode 100644 index 70c22b2..0000000 --- a/stm32/app/main.c +++ /dev/null @@ -1,135 +0,0 @@ -#include "project.h" -extern uint32_t dfu_flag; - -extern volatile uint32_t vector_table[]; - - -void reset_hardware (void) -{ - int i; - - rcc_periph_reset_pulse (RST_OTGFS); - rcc_periph_reset_pulse (RST_ETHMAC); - rcc_periph_reset_pulse (RST_AFIO); - rcc_periph_reset_pulse (RST_GPIOA); - rcc_periph_reset_pulse (RST_GPIOB); - rcc_periph_reset_pulse (RST_GPIOC); - rcc_periph_reset_pulse (RST_GPIOD); - rcc_periph_reset_pulse (RST_GPIOE); - rcc_periph_reset_pulse (RST_GPIOF); - rcc_periph_reset_pulse (RST_GPIOG); - rcc_periph_reset_pulse (RST_ADC1); - rcc_periph_reset_pulse (RST_ADC2); - rcc_periph_reset_pulse (RST_TIM1); - rcc_periph_reset_pulse (RST_SPI1); - rcc_periph_reset_pulse (RST_TIM8); - rcc_periph_reset_pulse (RST_USART1); - rcc_periph_reset_pulse (RST_ADC3); - rcc_periph_reset_pulse (RST_TIM15); - rcc_periph_reset_pulse (RST_TIM16); - rcc_periph_reset_pulse (RST_TIM17); - rcc_periph_reset_pulse (RST_TIM9); - rcc_periph_reset_pulse (RST_TIM10); - rcc_periph_reset_pulse (RST_TIM11); - rcc_periph_reset_pulse (RST_TIM2); - rcc_periph_reset_pulse (RST_TIM3); - rcc_periph_reset_pulse (RST_TIM4); - rcc_periph_reset_pulse (RST_TIM5); - rcc_periph_reset_pulse (RST_TIM6); - rcc_periph_reset_pulse (RST_TIM7); - rcc_periph_reset_pulse (RST_TIM12); - rcc_periph_reset_pulse (RST_TIM13); - rcc_periph_reset_pulse (RST_TIM14); - rcc_periph_reset_pulse (RST_WWDG); - rcc_periph_reset_pulse (RST_SPI2); - rcc_periph_reset_pulse (RST_SPI3); - rcc_periph_reset_pulse (RST_USART2); - rcc_periph_reset_pulse (RST_USART3); - rcc_periph_reset_pulse (RST_UART4); - rcc_periph_reset_pulse (RST_UART5); - rcc_periph_reset_pulse (RST_I2C1); - rcc_periph_reset_pulse (RST_I2C2); - rcc_periph_reset_pulse (RST_USB); - rcc_periph_reset_pulse (RST_CAN); - rcc_periph_reset_pulse (RST_CAN1); - rcc_periph_reset_pulse (RST_CAN2); - rcc_periph_reset_pulse (RST_BKP); - rcc_periph_reset_pulse (RST_PWR); - rcc_periph_reset_pulse (RST_DAC); - rcc_periph_reset_pulse (RST_CEC); - - - for (i = 0; i < NVIC_IRQ_COUNT; ++i) - nvic_disable_irq (i); - -} - - -int -main (void) -{ - - SCB_VTOR = (uint32_t) &vector_table; - asm volatile ("msr msp, %0"::"g" (vector_table[0])); - - reset_hardware(); - - - - // rcc_clock_setup_in_hsi_out_48mhz (); - //nvic_set_priority_grouping(NVIC_PriorityGroup_4); - - /*set up pll */ - rcc_clock_setup_in_hse_8mhz_out_72mhz(); - - /*turn on clocks to periferals */ - rcc_periph_clock_enable (RCC_GPIOA); - rcc_periph_clock_enable (RCC_GPIOB); - rcc_periph_clock_enable (RCC_GPIOC); - rcc_periph_clock_enable (RCC_AFIO); - rcc_periph_clock_enable (RCC_USART1); - rcc_periph_clock_enable (RCC_USART2); - rcc_periph_clock_enable (RCC_USART3); - rcc_periph_clock_enable (RCC_ADC1); - - dwt_enable_cycle_counter(); - - /*Change interrupt priorities so that USART trumps Timer trumps ATKBD */ - nvic_set_priority (NVIC_USART1_IRQ, 0x40); - nvic_set_priority (NVIC_USART2_IRQ, 0x40); - nvic_set_priority (NVIC_USART3_IRQ, 0x40); - nvic_set_priority (NVIC_SYSTICK_IRQ, 0x80); - nvic_set_priority (NVIC_EXTI9_5_IRQ, 0xc0); - - gpio_primary_remap (AFIO_MAPR_SWJ_CFG_JTAG_OFF_SW_ON, 0); - - led_init(); - - usart_init(); - ticker_init(); - - pic_init(); - ot_init(); - - onewire_init(); - - pressure_init(); - adc_init(); - - - printf ("STARTUP\r\n"); - printf ("%s\r\n", scm_version); - - - for (;;) { - if (!ring_empty (&rx1_ring)) - cmd_usart_dispatch(); - - temp_dispatch(); - pressure_dispatch(); - - } - - - return 0; -} diff --git a/stm32/app/ot.c b/stm32/app/ot.c deleted file mode 100644 index ba2b7d9..0000000 --- a/stm32/app/ot.c +++ /dev/null @@ -1,441 +0,0 @@ -#include "project.h" - - -typedef struct { - uint8_t flags: 5; - uint8_t type: 3; - uint8_t data[2]; -} OT_Msg; - - -#define FLAGS_PENDING_THM 1U << 0 -#define FLAGS_PROCESSED_THM 1U << 1 -#define FLAGS_PENDING_USR 1U << 2 -#define FLAGS_PROCESSED_USR 1U << 3 -#define FLAGS_PENDING_OVR 1U << 4 - -#define FLAGS_PENDING (FLAGS_PENDING_THM|FLAGS_PENDING_USR | FLAGS_PENDING_OVR) - -#define IDX_MAX 0x300 - -static OT_Msg ot_thm_req[IDX_MAX]; -static OT_Msg ot_blr_rsp[IDX_MAX]; - -#define OT_NEXT(a) (((a) < 0x2ff) ? ((a)+1):0) -#define OT_INDEX(t,id) ((((unsigned) (t) ) <<8 ) | ((unsigned) (id))) -#define OT_IDX_TO_ID(idx) ((idx) & 0xff) -#define OT_IDX_TO_TYPE(idx) ((idx) >> 8) - -static unsigned ot_req_idx; -static unsigned in_flight_req_type; -static unsigned blr_backoff; -static unsigned ot_status_wdt; - - -#define OT_READ_DATA 0x0 -#define OT_WRITE_DATA 0x1 -#define OT_INVALID_DATA 0x2 -#define OT_READ_ACK 0x4 -#define OT_WRITE_ACK 0x5 -#define OT_DATA_INVALID 0x6 -#define OT_UNKNOWN_DATAID 0x7 - - -#define OT_IDX_STATUS 0 -/*TX*/ -#define OT_IDX_STATUS_BIT_ENABLE_CH (1U << 0) -#define OT_IDX_STATUS_BIT_ENABLE_DHW (1U << 1) - -/*RX*/ -#define OT_IDX_STATUS_BIT_FAULT (1U << 0) -#define OT_IDX_STATUS_BIT_CH_MODE (1U << 1) -#define OT_IDX_STATUS_BIT_DHW_MODE (1U << 2) -#define OT_IDX_STATUS_BIT_FLAME (1U << 3) - -#define OT_IDX_CONTROL_SETPOINT 1 -#define OT_IDX_DHW_SETPOINT 56 - - -#define OT_IDX_CH_WATER_PRESSURE 18 -#define OT_IDX_RETURN_WATER_TEMP 28 -#define OT_IDX_SUPPLY_INLET_TEMP 80 - - - -unsigned ot_override_ch = 0; -unsigned ot_override_dhw = 0; - - -static inline int parity (uint8_t v) -{ - return (0x6996u >> ((v ^ (v >> 4)) & 0xf)) & 1; -} - -static void ot_parity (uint8_t *data) -{ - int p; - p = parity (data[0]); - p ^= parity (data[1]); - p ^= parity (data[2]); - p ^= parity (data[3]); - - if (p) data[0] ^= 0x80; -} - - -static const char *type_str[8] = { - ">Read Data", - ">Write Data", - ">Invalid Data", - ">Reserved", - "> 4) & 7; - - - printf ("%s%02x%02x%02x%02x %s %s\r\n", - who, - msg[0], - msg[1], - msg[2], - msg[3], - type_str[type], - what - ); -} - -static void send_reply_to_thm (unsigned idx) -{ - uint8_t reply[4]; - - if (ot_tx_thm (NULL)) return; - - reply[0] = ot_blr_rsp[idx].type << 4; - reply[1] = OT_IDX_TO_ID (idx); - reply[2] = ot_blr_rsp[idx].data[0]; - reply[3] = ot_blr_rsp[idx].data[1]; - - ot_debug ("B", reply, ""); - - ot_parity (reply); - ot_tx_thm (reply); - - ot_blr_rsp[idx].flags &= ~FLAGS_PROCESSED_THM; - -} - - -static int send_req_to_blr (unsigned idx) -{ - uint8_t req[4]; - - if (ot_tx_blr (NULL)) return -1; - - - - req[0] = ot_thm_req[ot_req_idx].type << 4; - req[1] = OT_IDX_TO_ID (ot_req_idx); - req[2] = ot_thm_req[ot_req_idx].data[0]; - req[3] = ot_thm_req[ot_req_idx].data[1]; - - ot_parity (req); - ot_debug (" S", req, ""); - - return ot_tx_blr (req); -} - - - - -void ot_rx_thm (uint8_t *msg, int error) -{ - unsigned type = (msg[0] >> 4) & 7; - unsigned id = msg[1]; - - unsigned idx; - - if (error) return; - - if (type > 2) { - ot_debug ("T", msg, "message type invalid for thermostat"); - return; - } - - if (ot_override_ch) { - if ((id == OT_IDX_STATUS) && (type == OT_READ_DATA)) /* Turn on heating */ - msg[2] |= OT_IDX_STATUS_BIT_ENABLE_CH; - - if ((id == OT_IDX_CONTROL_SETPOINT) && (type == OT_WRITE_DATA)) /* set water temp */ - msg[2] = ot_override_ch; - - } - - if (ot_override_dhw) { - if ((id == OT_IDX_STATUS) && (type == OT_READ_DATA)) /* Turn on hotwater */ - msg[2] |= OT_IDX_STATUS_BIT_ENABLE_DHW; - - if ((id == OT_IDX_DHW_SETPOINT) && (type == OT_WRITE_DATA)) /* set water temp */ - msg[2] = ot_override_dhw; - - } - - if ((id == OT_IDX_STATUS) && (type == OT_READ_DATA)) - ot_status_wdt = 0; - - ot_debug ("T", msg, ""); - - idx = OT_INDEX (type, id); - - if (ot_blr_rsp[idx].flags & FLAGS_PROCESSED_THM) - send_reply_to_thm (idx); - - else { - ot_thm_req[idx].type = type; - ot_thm_req[idx].data[0] = msg[2];; - ot_thm_req[idx].data[1] = msg[3];; - ot_thm_req[idx].flags |= FLAGS_PENDING_THM; - } -} - -static int ot_fake_read_ack (unsigned id, uint8_t *msg) -{ - unsigned t; - - switch (id) { - case OT_IDX_CH_WATER_PRESSURE: - t = pressure_ch(); - - if (!t) return -1; - - break; - - case OT_IDX_RETURN_WATER_TEMP: - t = temp_ch_return(); - - if (!t) return -1; - - break; - - case OT_IDX_SUPPLY_INLET_TEMP: - t = temp_supply_inlet(); - - if (!t) return -1; - - break; - - default: - return -1; - } - - - - msg[0] = OT_READ_ACK << 4; - msg[1] = id; - msg[2] = t >> 8; - msg[3] = t & 0xff; - - return 0; -} - - -void ot_rx_blr (uint8_t *msg, int error) -{ - unsigned type = (msg[0] >> 4) & 7; - unsigned id = msg[1]; - - unsigned idx; - - if (error) return; - - ot_debug (" A", msg, ""); - - idx = OT_INDEX (in_flight_req_type, id); - - if ((in_flight_req_type == OT_READ_DATA) && (type != OT_READ_ACK)) { - if (!ot_fake_read_ack (id, msg)) { - ot_debug (" A", msg, " (faked)"); - type = (msg[0] >> 4) & 7; - } - } - - ot_blr_rsp[idx].type = type; - ot_blr_rsp[idx].data[0] = msg[2];; - ot_blr_rsp[idx].data[1] = msg[3];; - ot_blr_rsp[idx].flags |= FLAGS_PROCESSED_THM | FLAGS_PROCESSED_USR; - - if (ot_thm_req[idx].flags & FLAGS_PENDING_THM) { - ot_thm_req[idx].flags &= ~FLAGS_PENDING_THM; - send_reply_to_thm (idx); - } - - if (ot_thm_req[idx].flags & FLAGS_PENDING_USR) { - ot_thm_req[idx].flags &= ~FLAGS_PENDING_USR; - //send_reply_to_usr (idx); - } - - if (ot_thm_req[idx].flags & FLAGS_PENDING_OVR) - ot_thm_req[idx].flags &= ~FLAGS_PENDING_OVR; - - blr_backoff = 0; - -} - - - -static void ot_boiler_worker (void) -{ - unsigned i; - - if (blr_backoff) { - blr_backoff--; - return; - } - - if (ot_tx_blr (NULL)) return; - - - for (i = 0; i < IDX_MAX; ++i, ot_req_idx = OT_NEXT (ot_req_idx)) { - - if (ot_thm_req[ot_req_idx].flags & FLAGS_PENDING) { - - if (!send_req_to_blr (ot_req_idx)) { - ot_thm_req[ot_req_idx].flags &= ~FLAGS_PENDING; - in_flight_req_type = OT_IDX_TO_TYPE (ot_req_idx); - blr_backoff = 10; - ot_req_idx = OT_NEXT (ot_req_idx); - return; - } - } - - } - -} - - -static void ot_request (unsigned flags, unsigned type, unsigned id, uint8_t *data) -{ - unsigned idx = OT_INDEX (type, id); - - ot_thm_req[idx].type = type; - - if (data) { - ot_thm_req[idx].data[0] = data[0]; - ot_thm_req[idx].data[1] = data[1]; - } else { - ot_thm_req[idx].data[0] = 0; - ot_thm_req[idx].data[1] = 0; - } - - ot_thm_req[idx].flags |= FLAGS_PENDING_USR; - -} - -void ot_request_usr (unsigned type, unsigned id, uint8_t *data) -{ - ot_request (FLAGS_PENDING_USR, type, id, data); -} -static void ot_request_ovr (unsigned type, unsigned id, uint8_t *data) -{ - ot_request (FLAGS_PENDING_OVR, type, id, data); -} - - - -static void ot_force_status (void) -{ - uint8_t data[2] = { (ot_override_ch ? OT_IDX_STATUS_BIT_ENABLE_CH : 0) | (ot_override_dhw ? OT_IDX_STATUS_BIT_ENABLE_DHW : 0), 0}; - - ot_request_ovr (OT_READ_DATA, OT_IDX_STATUS, data); - -} - -static void ot_30s_ticker (void) -{ - uint8_t data[2]; - uint8_t msg[4]; - - printf ("Q ot ticker - push control flags and log data"); - - if (ot_override_ch) { - data[0] = ot_override_ch; - data[1] = 0; - ot_request_ovr (OT_WRITE_DATA, OT_IDX_CONTROL_SETPOINT, data); - } - - if (ot_override_dhw) { - - data[0] = ot_override_dhw; - data[1] = 0; - ot_request_ovr (OT_WRITE_DATA, OT_IDX_DHW_SETPOINT, data); - } - - - if (!ot_fake_read_ack (OT_IDX_CH_WATER_PRESSURE, msg)) - ot_debug ("B", msg, " (fake reply)"); - - if (!ot_fake_read_ack (OT_IDX_RETURN_WATER_TEMP, msg)) - ot_debug ("B", msg, " (fake reply)"); - - if (!ot_fake_read_ack (OT_IDX_SUPPLY_INLET_TEMP, msg)) - ot_debug ("B", msg, " (fake reply)"); - -} - - - - -static void ot_4hz_ticker (void) -{ - static unsigned i; - i++; - - ot_boiler_worker(); - - ot_status_wdt++; - - if (ot_status_wdt > 120) { - printf ("Q forcing status packet\r\n"); - ot_force_status(); - ot_status_wdt = 0; - } - - - if (i >= 120) { - ot_30s_ticker(); - i = 0; - } - - - led_yellow_set (ot_blr_rsp[OT_INDEX (OT_READ_DATA, OT_IDX_STATUS)].data[1] & OT_IDX_STATUS_BIT_FAULT); - led_green1_set (ot_blr_rsp[OT_INDEX (OT_READ_DATA, OT_IDX_STATUS)].data[1] & OT_IDX_STATUS_BIT_CH_MODE); - led_green2_set (ot_blr_rsp[OT_INDEX (OT_READ_DATA, OT_IDX_STATUS)].data[1] & OT_IDX_STATUS_BIT_DHW_MODE); - led_red_set (ot_blr_rsp[OT_INDEX (OT_READ_DATA, OT_IDX_STATUS)].data[1] & OT_IDX_STATUS_BIT_FLAME); - -} - - -void ot_tick (void) -{ - static unsigned i; - i++; - - if (i >= 500) { - ot_4hz_ticker(); - i = 0; - } - - -} - - -void ot_init (void) -{ - ot_phy_rx_init(); - ot_phy_tx_init(); -} diff --git a/stm32/app/ot_phy_rx.c b/stm32/app/ot_phy_rx.c deleted file mode 100644 index 9348d24..0000000 --- a/stm32/app/ot_phy_rx.c +++ /dev/null @@ -1,177 +0,0 @@ -#include "project.h" - -#define OT_THM_IN GPIO6 -#define OT_THM_IN_PORT GPIOB - -#define OT_BLR_IN GPIO7 -#define OT_BLR_IN_PORT GPIOB - - -#define OT_RX_IRQ NVIC_EXTI9_5_IRQ - - -typedef struct rx_phy { - uint32_t last_cycle; - int last_v; - unsigned half_bits; - unsigned data_bits; - int frame_error; - uint8_t data[10]; - int parity; -} RX_Phy; - - -static RX_Phy p_thm, p_blr; - - -static uint32_t -cycle_diff (uint32_t a, uint32_t b) -{ - return b - a; -} - - - - -static void ot_phy_rx_bit (RX_Phy *p) -{ - - - if (p->half_bits & 1) - p->frame_error = 1; - - if (!p->half_bits) { - if (!p->last_v) - p->frame_error = 1; - - return; - } - - if (p->data_bits < 32) { - if (p->last_v) p->data[p->data_bits >> 3] |= 0x80 >> (p->data_bits & 7); - - p->parity ^= p->last_v; - } else if (p->data_bits == 32) { - if ((!p->last_v) || (p->parity)) - p->frame_error = 1; - - led_blink(); - - if (p == &p_thm) - ot_rx_thm (p->data, p->frame_error); - else - ot_rx_blr (p->data, p->frame_error); - } - - - p->data_bits++; - -} - - -static void ot_phy_rx_worker (RX_Phy *p, int v) -{ - uint32_t now, diff; - - if (v == p->last_v) return; - - - now = dwt_read_cycle_counter(); - diff = cycle_diff (p->last_cycle, now); - - - if (diff < 10000) return; - - if (diff < 50000) { - if (! (p->half_bits & 1)) ot_phy_rx_bit (p); - - p->half_bits++; - } else if (diff < 85000) { - p->half_bits++; - ot_phy_rx_bit (p); - p->half_bits++; - } else { - p->parity = 0; - p->half_bits = 0; - p->frame_error = 0; - p->data_bits = 0; - memset (p->data, 0, sizeof (p->data)); - } - - p->last_cycle = now; - p->last_v = v; -} - - - -void -exti9_5_isr (void) -{ - int v; - - if (EXTI_PR & OT_THM_IN) { - EXTI_PR = OT_THM_IN; - v = !GET (OT_THM_IN); - -#if 0 - - if (v) - CLEAR (OT_BLR_OUT); - else - SET (OT_BLR_OUT); - -#endif - - ot_phy_rx_worker (&p_thm, v); - - - } - - if (EXTI_PR & OT_BLR_IN) { - EXTI_PR = OT_BLR_IN; - v = !GET (OT_BLR_IN); - -#if 0 - - if (v) - CLEAR (OT_THM_OUT); - else - SET (OT_THM_OUT); - -#endif - - ot_phy_rx_worker (&p_blr, v); - } - - return; -} - - -void ot_phy_rx_tick (void) -{ -} - - - -void ot_phy_rx_init (void) -{ - MAP_INPUT_PU (OT_THM_IN); - MAP_INPUT_PU (OT_BLR_IN); - - - exti_select_source (OT_THM_IN, OT_THM_IN_PORT); - exti_set_trigger (OT_THM_IN, EXTI_TRIGGER_BOTH); - exti_enable_request (OT_THM_IN); - exti_reset_request (OT_THM_IN); - - exti_select_source (OT_BLR_IN, OT_BLR_IN_PORT); - exti_set_trigger (OT_BLR_IN, EXTI_TRIGGER_BOTH); - exti_enable_request (OT_BLR_IN); - exti_reset_request (OT_BLR_IN); - - nvic_enable_irq (OT_RX_IRQ); - -} - - - diff --git a/stm32/app/ot_phy_tx.c b/stm32/app/ot_phy_tx.c deleted file mode 100644 index 36fbcc5..0000000 --- a/stm32/app/ot_phy_tx.c +++ /dev/null @@ -1,121 +0,0 @@ -#include "project.h" - -#define OT_THM_OUT GPIO9 -#define OT_THM_OUT_PORT GPIOB - -#define OT_BLR_OUT GPIO8 -#define OT_BLR_OUT_PORT GPIOB - - -typedef struct tx_phy { - int busy; - unsigned half_bit; - uint8_t data[4]; -} TX_Phy; - - -static TX_Phy p_thm, p_blr; - - - -#if 0 -static uint32_t -cycle_diff (uint32_t a, uint32_t b) -{ - return b - a; -} -#endif - - -static int ot_phy_tx_worker (TX_Phy *p) -{ - int ret = p->half_bit & 1; - unsigned bit; - - - if (p->half_bit < 2) - ret ^= 1; - - else if (p->half_bit < 66) { - bit = (p->half_bit >> 1) - 1; - - if (p->data[bit >> 3] & (0x80 >> (bit & 7))) - ret ^= 1; - } else if (p->half_bit < 68) - - ret ^= 1; - - p->half_bit++; - - if (p->half_bit == 68) { - p->half_bit = 0; - p->busy = 0; - } - - return ret; -} - - -void ot_phy_tx_tick (void) -{ - int v; - - if (p_thm.busy) { - v = ot_phy_tx_worker (&p_thm); - - if (v) - CLEAR (OT_THM_OUT); - else - SET (OT_THM_OUT); - } - - - if (p_blr.busy) { - v = ot_phy_tx_worker (&p_blr); - - if (v) - CLEAR (OT_BLR_OUT); - else - SET (OT_BLR_OUT); - - } -} - - -int ot_tx_thm (uint8_t *data) -{ - if (p_thm.busy) return -1; - - if (!data) return 0; - - led_blink(); - - memcpy (p_thm.data, data, sizeof (p_thm.data)); - p_thm.busy = 1; - - return 0; -} - - -int ot_tx_blr (uint8_t *data) -{ - - if (p_blr.busy) return -1; - - if (!data) return 0; - - led_blink(); - - memcpy (p_blr.data, data, sizeof (p_blr.data)); - p_blr.busy = 1; - - return 0; -} - - - -void ot_phy_tx_init (void) -{ - MAP_OUTPUT_PP (OT_THM_OUT); - MAP_OUTPUT_PP (OT_BLR_OUT); -} diff --git a/stm32/app/pic.c b/stm32/app/pic.c deleted file mode 100644 index b478766..0000000 --- a/stm32/app/pic.c +++ /dev/null @@ -1,52 +0,0 @@ -#include "project.h" - -#define PIC_RESET GPIO1 -#define PIC_RESET_PORT GPIOB - - -static void pic_reset (void) -{ - SET (PIC_RESET); - delay_ms (1); - CLEAR (PIC_RESET); -} - - -void pic_passthru (void) -{ - uint8_t c; - - printf ("\r\nPIC MODE\r\n"); - usart1_drain(); - block_stdio = 1; - - while (!ring_read_byte (&rx3_ring, &c)); - - pic_reset(); - - - for (;;) { - if (!ring_read_byte (&rx1_ring, &c)) - usart3_queue (c); - - if (!ring_read_byte (&rx3_ring, &c)) - usart1_queue (c); - - } -} - - - - - - - -void pic_init (void) -{ - MAP_OUTPUT_PP (PIC_RESET); - - pic_reset(); - -} - - diff --git a/stm32/app/pins.h b/stm32/app/pins.h deleted file mode 100644 index 9ea01cf..0000000 --- a/stm32/app/pins.h +++ /dev/null @@ -1,53 +0,0 @@ -#ifndef _PINS_H_ -#define _PINS_H_ - -/* st seem to change these with every chip revision */ - -#define MAP_AF(a) do { \ - gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_PUSHPULL, a ); \ - } while (0) - -/* STM32F1 doesn't have AF pull up, but also doesn't disconnect af inputs so just use regular pull up */ -#define MAP_AF_PU(a) do { \ - gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, a); \ - gpio_set( a ## _PORT, a); \ - } while (0) - -#define MAP_AF_OD(a) do { \ - gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_ALTFN_OPENDRAIN, a ); \ - } while (0) - - -#define MAP_OUTPUT_PP(a) do { \ - gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_PUSHPULL, a ); \ - } while (0) - - -#define MAP_OUTPUT_OD(a) do { \ - gpio_set_mode( a ## _PORT, GPIO_MODE_OUTPUT_50_MHZ, GPIO_CNF_OUTPUT_OPENDRAIN, a ); \ - } while (0) - - -/* STM32F1 madly uses the output register to drive the other end of the resistor, so pull up */ -/* requires us to write a 1 there */ - -#define MAP_INPUT_PU(a) do { \ - gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_PULL_UPDOWN, a); \ - gpio_set( a ## _PORT, a); \ - } while (0) - - -#define MAP_INPUT(a) do { \ - gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_FLOAT, a); \ - } while (0) - -#define MAP_ANALOG(a) do { \ - gpio_set_mode( a ## _PORT, GPIO_MODE_INPUT, GPIO_CNF_INPUT_ANALOG, a); \ - } while (0) - - -#define CLEAR(a) gpio_clear( a ## _PORT, a) -#define SET(a) gpio_set( a ## _PORT, a) -#define GET(a) gpio_get( a ## _PORT, a) - -#endif diff --git a/stm32/app/pressure.c b/stm32/app/pressure.c deleted file mode 100644 index 6b90d93..0000000 --- a/stm32/app/pressure.c +++ /dev/null @@ -1,79 +0,0 @@ -#include "project.h" - -#define PRESSURE GPIO0 -#define PRESSURE_PORT GPIOA -#define PRESSURE_CHANNEL ADC_CHANNEL0 -#define VREF_CHANNEL ADC_CHANNEL17 - -static unsigned poke; - -static int pressure; - -uint16_t pressure_ch (void) -{ - return pressure; -} - - - - -void pressure_tick (void) -{ - static unsigned ticker; - - ticker++; - - if (ticker < MS_TO_TICKS (2500)) - return; - - ticker = 0; - poke = 1; - -} - - - - -void pressure_dispatch (void) -{ - int v, r; - - if (!poke) return; - - poke = 0; - - if (adc_calibrate()) { - pressure = 0; - return; - } - - v = adc_convert (PRESSURE_CHANNEL); - r = adc_convert (VREF_CHANNEL); - - - /* r is 1.25 volts, transducer is 0.5V -> 0 psi 4.5V -> 100psi */ - /* 100psi is 6.8947573 bar, and we want 256ths of bar */ - - if (!r) { - pressure = 0; - return; - } - - pressure = ((v * 552) / r) - 221; - - if (pressure < 0) pressure = 0; - - printf ("QP: %d %d %d\r\n", v, r, (pressure * 100) / 256); - -} - - - -void pressure_init (void) -{ - MAP_ANALOG (PRESSURE); -} - - - - diff --git a/stm32/app/project.h b/stm32/app/project.h deleted file mode 100644 index d1bfa52..0000000 --- a/stm32/app/project.h +++ /dev/null @@ -1,30 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#include "ring.h" -#include "pins.h" -#include "1wire.h" - - -#define US (72) -#define MS (US * 1000) -#define HZ (MS * 1000) - -#define MS_TO_TICKS(a) ((a) *2) - - -#include "prototypes.h" diff --git a/stm32/app/prototypes.h b/stm32/app/prototypes.h deleted file mode 100644 index 25a1627..0000000 --- a/stm32/app/prototypes.h +++ /dev/null @@ -1,103 +0,0 @@ -/* main.c */ -extern void reset_hardware(void); -extern int main(void); -/* ot.c */ -extern unsigned ot_override_ch; -extern unsigned ot_override_dhw; -extern void ot_rx_thm(uint8_t *msg, int error); -extern void ot_rx_blr(uint8_t *msg, int error); -extern void ot_request_usr(unsigned type, unsigned id, uint8_t *data); -extern void ot_tick(void); -extern void ot_init(void); -/* ot_phy_rx.c */ -extern void exti9_5_isr(void); -extern void ot_phy_rx_tick(void); -extern void ot_phy_rx_init(void); -/* ot_phy_tx.c */ -extern void ot_phy_tx_tick(void); -extern int ot_tx_thm(uint8_t *data); -extern int ot_tx_blr(uint8_t *data); -extern void ot_phy_tx_init(void); -/* led.c */ -extern void led_red_set(int i); -extern void led_green1_set(int i); -extern void led_green2_set(int i); -extern void led_blink(void); -extern void led_yellow_set(int i); -extern void led_tick(void); -extern void led_init(void); -/* ticker.c */ -extern void delay_us(uint32_t d); -extern void sys_tick_handler(void); -extern void delay_ms(uint32_t d); -extern void ticker_init(void); -/* usart.c */ -extern ring_t rx1_ring; -extern ring_t tx1_ring; -extern ring_t rx3_ring; -extern ring_t tx3_ring; -extern void usart1_isr(void); -extern void usart1_queue(uint8_t d); -extern void usart1_drain(void); -extern int usart1_write(char *ptr, int len, int blocking); -extern void usart3_isr(void); -extern void usart3_queue(uint8_t d); -extern void usart3_drain(void); -extern int usart3_write(char *ptr, int len, int blocking); -extern void usart_init(void); -/* ring.c */ -extern void ring_init(volatile ring_t *r, uint8_t *buf, size_t len); -extern int ring_write_byte(volatile ring_t *r, uint8_t c); -extern int ring_read_byte(volatile ring_t *r, uint8_t *c); -extern int ring_write(volatile ring_t *r, uint8_t *buf, size_t len, int blocking); -extern int ring_empty(volatile ring_t *r); -/* stdio.c */ -extern unsigned block_stdio; -extern int _open(const char *name, int flags, int mode); -extern int _close(int file); -extern int _write(int file, char *buf, int nbytes); -extern int _read(int file, char *buf, int nbytes); -extern int _lseek(int file, int offset, int whence); -extern int isatty(int file); -extern void stdio_drain(void); -/* util.c */ -extern char int_to_hex_char(int a); -/* commit.c */ -extern char scm_version[]; -/* cmd.c */ -extern void cmd_usart_dispatch(void); -/* pic.c */ -extern void pic_passthru(void); -extern void pic_init(void); -/* 1wire.c */ -extern void onewire_tick(void); -extern int onewire_reset(void); -extern void onewire_write_byte(uint8_t v); -extern uint8_t onewire_read_byte(void); -extern void onewire_read_bytes(uint8_t *buf, int n); -extern void onewire_write_bytes(const uint8_t *buf, int n); -extern int onewire_select(const Onewire_addr *a); -extern int onewire_reset_and_select(const Onewire_addr *a); -extern int onewire_wait_complete(unsigned timeout); -extern int onewire_check_crc(uint8_t *buf, int n, uint8_t v); -extern int onewire_search(void); -extern void onewire_init(void); -/* temp.c */ -extern void temp_tick(void); -extern void temp_dispatch(void); -extern uint16_t temp_ch_return(void); -extern uint16_t temp_supply_inlet(void); -/* ds1820.c */ -extern int ds1820_read(const Onewire_addr *a, int *temp); -/* pressure.c */ -extern uint16_t pressure_ch(void); -extern void pressure_tick(void); -extern void pressure_dispatch(void); -extern void pressure_init(void); -/* adc.c */ -extern void adc_dump(void); -extern volatile unsigned timeout; -extern void adc_tick(void); -extern int adc_calibrate(void); -extern unsigned adc_convert(unsigned channel); -extern void adc_init(void); diff --git a/stm32/app/ring.c b/stm32/app/ring.c deleted file mode 100644 index 973f345..0000000 --- a/stm32/app/ring.c +++ /dev/null @@ -1,77 +0,0 @@ -#include "project.h" - - -static inline size_t -ring_next (volatile ring_t *r, size_t p) -{ - p++; - - if (p >= r->size) - p -= r->size; - - return p; -} - -void -ring_init (volatile ring_t *r, uint8_t *buf, size_t len) -{ - r->data = buf; - r->size = len; - r->write = 0; - r->read = 0; -} - -int -ring_write_byte (volatile ring_t *r, uint8_t c) -{ - size_t n = ring_next (r, r->write); - - if (n == r->read) - return -EAGAIN; - - r->data[r->write] = c; - - r->write = n; - - return 0; -} - - -int -ring_read_byte (volatile ring_t *r, uint8_t *c) -{ - size_t n = ring_next (r, r->read); - - if (r->read == r->write) - return -EAGAIN; - - *c = r->data[r->read]; - r->read = n; - - return 0; -} - -int -ring_write (volatile ring_t *r, uint8_t *buf, size_t len, int blocking) -{ - while (len--) { - if (blocking) { - while (ring_write_byte (r, *buf)); - - buf++; - } else { - if (ring_write_byte (r, * (buf++))) - return -EAGAIN; - } - } - - return 0; -} - - - -int -ring_empty (volatile ring_t *r) -{ - return (r->read == r->write) ? 1 : 0; -} diff --git a/stm32/app/ring.h b/stm32/app/ring.h deleted file mode 100644 index a329354..0000000 --- a/stm32/app/ring.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef _RING_H_ -#define _RING_H_ - -typedef struct ring { - uint8_t *data; - size_t size; - size_t write; - size_t read; -} ring_t; - -#endif - - diff --git a/stm32/app/stdio.c b/stm32/app/stdio.c deleted file mode 100644 index bcdd5c7..0000000 --- a/stm32/app/stdio.c +++ /dev/null @@ -1,77 +0,0 @@ -#include "project.h" - -unsigned block_stdio; - -int -_open (const char *name, int flags, int mode) -{ - errno = ENOSYS; - return -1; /* Always fails */ - -} /* _open () */ - -int -_close (int file) -{ - errno = EBADF; - return -1; /* Always fails */ - -} /* _close () */ - -int -_write (int file, char *buf, int nbytes) -{ - - int ret = nbytes; - - if (!block_stdio) - ret = usart1_write (buf, nbytes, 1); - - if (ret < 0) { - errno = -ret; - return -1; - } - - return ret; -} /* _write () */ - - -int -_read (int file, char *buf, int nbytes) -{ - - errno = -EAGAIN; - return -1; /* EOF */ - -} /* _read () */ - -#if 0 -int -_fstat (int file, struct stat *st) -{ - st->st_mode = S_IFCHR; - return 0; - -} /* _fstat () */ -#endif - -int -_lseek (int file, int offset, int whence) -{ - return 0; - -} /* _lseek () */ - -int -isatty (int file) -{ - return 1; - -} /* _isatty () */ - - -void -stdio_drain (void) -{ - usart1_drain(); -} diff --git a/stm32/app/temp.c b/stm32/app/temp.c deleted file mode 100644 index 4548d01..0000000 --- a/stm32/app/temp.c +++ /dev/null @@ -1,76 +0,0 @@ -#include "project.h" - - -#define N_SENSORS 2 - -#define SENSOR_INDEX_CH_RETURN 0 -#define SENSOR_INDEX_SUPPLY_INLET 1 - -static const Onewire_addr s_addr[N_SENSORS] = { - [0] = {{0x28, 0x60, 0x06, 0x53, 0x03, 0x00, 0x00, 0xf5}}, - [1] = {{0x28, 0xa4, 0x08, 0x53, 0x03, 0x00, 0x00, 0x14}}, -}; - - -static int s_temp[N_SENSORS]; - -static unsigned poke; - - -void temp_tick (void) -{ - static unsigned ticker; - - ticker++; - - if (ticker < 3000) - return; - - ticker = 0; - poke = 1; - -} - - - - - -void temp_dispatch (void) -{ - static unsigned sensor; - - if (!poke) return; - - poke = 0; - - if (sensor < N_SENSORS) { - if (ds1820_read (&s_addr[sensor], &s_temp[sensor])) - s_temp[sensor] = 0; - - - printf ("Q1W: sensor %d temp %d\r\n", sensor, (s_temp[sensor] * 100) / 256); - - - sensor++; - } else { -#if 0 - onewire_search(); -#endif - } - - if (sensor == N_SENSORS) - sensor = 0; - - -} - - - -uint16_t temp_ch_return (void) -{ - return s_temp[SENSOR_INDEX_CH_RETURN]; -} -uint16_t temp_supply_inlet (void) -{ - return s_temp[SENSOR_INDEX_SUPPLY_INLET]; -} diff --git a/stm32/app/ticker.c b/stm32/app/ticker.c deleted file mode 100644 index e47f4eb..0000000 --- a/stm32/app/ticker.c +++ /dev/null @@ -1,103 +0,0 @@ -#include "project.h" - - -static volatile uint32_t delay_hms_count; -static volatile uint32_t ticks; -static uint32_t scale = 7; - -void -delay_us (uint32_t d) -{ - d *= scale; - - while (d--) - __asm__ ("nop"); -} - -void -sys_tick_handler (void) -{ - if (delay_hms_count) - delay_hms_count--; - - led_tick(); - ot_phy_tx_tick(); - ot_tick(); - onewire_tick(); - temp_tick(); - adc_tick(); - pressure_tick(); - - - ticks++; -} - - - -void -delay_ms (uint32_t d) -{ - delay_hms_count = d << 1; - - while (delay_hms_count); -} - -#if 0 -int -timed_out (uint32_t then, unsigned int ms) -{ - then = ticks - then; - - if (then > ms) - return 1; - - return 0; -} - -int -timed_out_cycles (uint32_t then, unsigned int cycles) -{ - then = dwt_read_cycle_counter() - then; - - if (then > cycles) - return 1; - - return 0; -} -#endif - - - -void -ticker_init (void) -{ - uint32_t v, w; - - /*Start periodic timer */ - - systick_set_clocksource (STK_CSR_CLKSOURCE_AHB_DIV8); - /* 72MHz / 8 = > 9Mhz */ - systick_set_reload (4500); - /* 9MHz / 4500 => 2kHz */ - systick_interrupt_enable(); - systick_counter_enable(); - - /*Calibrate the delay loop */ - - - - do { - scale--; - v = ticks; - - while (v == ticks); - - delay_us (500); - w = ticks; - v++; - w -= v; - } while (w); - - - -} diff --git a/stm32/app/usart.c b/stm32/app/usart.c deleted file mode 100644 index 28676a0..0000000 --- a/stm32/app/usart.c +++ /dev/null @@ -1,175 +0,0 @@ -#include "project.h" - -#define BUFFER_SIZE 256 - - -#define USART1_TX GPIO_USART1_TX -#define USART1_TX_PORT GPIOA - -#define USART1_RX GPIO_USART1_RX -#define USART1_RX_PORT GPIOA - -#define USART3_TX GPIO_USART3_TX -#define USART3_TX_PORT GPIOB - -#define USART3_RX GPIO_USART3_RX -#define USART3_RX_PORT GPIOB - - - -ring_t rx1_ring; -static uint8_t rx1_ring_buf[BUFFER_SIZE]; - -ring_t tx1_ring; -static uint8_t tx1_ring_buf[BUFFER_SIZE]; - -ring_t rx3_ring; -static uint8_t rx3_ring_buf[BUFFER_SIZE]; - -ring_t tx3_ring; -static uint8_t tx3_ring_buf[BUFFER_SIZE]; - -void -usart1_isr (void) -{ - uint8_t data; - - /* Check if we were called because of RXNE. */ - if (((USART_CR1 (USART1) & USART_CR1_RXNEIE) != 0) && - ((USART_SR (USART1) & USART_SR_RXNE) != 0)) { - data = usart_recv (USART1); - ring_write_byte (&rx1_ring, data); - } - - /* Check if we were called because of TXE. */ - if (((USART_CR1 (USART1) & USART_CR1_TXEIE) != 0) && - ((USART_SR (USART1) & USART_SR_TXE) != 0)) { - - if (ring_read_byte (&tx1_ring, &data)) { - /*No more data, Disable the TXE interrupt, it's no longer needed. */ - usart_disable_tx_interrupt (USART1); - } else - usart_send_blocking (USART1, data); - } - -} - -void -usart1_queue (uint8_t d) -{ - ring_write_byte (&tx1_ring, d); - usart_enable_tx_interrupt (USART1); -} - -void -usart1_drain (void) -{ - while (!ring_empty (&tx1_ring)); -} - - -int -usart1_write (char *ptr, int len, int blocking) -{ - int ret; - - ret = ring_write (&tx1_ring, (uint8_t *) ptr, len, blocking); - usart_enable_tx_interrupt (USART1); - return ret; -} - - -void -usart3_isr (void) -{ - uint8_t data; - - /* Check if we were called because of RXNE. */ - if (((USART_CR1 (USART3) & USART_CR1_RXNEIE) != 0) && - ((USART_SR (USART3) & USART_SR_RXNE) != 0)) { - data = usart_recv (USART3); - ring_write_byte (&rx3_ring, data); - } - - /* Check if we were called because of TXE. */ - if (((USART_CR1 (USART3) & USART_CR1_TXEIE) != 0) && - ((USART_SR (USART3) & USART_SR_TXE) != 0)) { - - if (ring_read_byte (&tx3_ring, &data)) { - /*No more data, Disable the TXE interrupt, it's no longer needed. */ - usart_disable_tx_interrupt (USART3); - } else - usart_send_blocking (USART3, data); - } - -} - -void -usart3_queue (uint8_t d) -{ - ring_write_byte (&tx3_ring, d); - usart_enable_tx_interrupt (USART3); -} - -void -usart3_drain (void) -{ - while (!ring_empty (&tx3_ring)); -} - - -int -usart3_write (char *ptr, int len, int blocking) -{ - int ret; - - ret = ring_write (&tx3_ring, (uint8_t *) ptr, len, blocking); - usart_enable_tx_interrupt (USART3); - return ret; -} - - -void -usart_init (void) -{ - ring_init (&rx1_ring, rx1_ring_buf, sizeof (rx1_ring_buf)); - ring_init (&tx1_ring, tx1_ring_buf, sizeof (tx1_ring_buf)); - - nvic_enable_irq (NVIC_USART1_IRQ); - - MAP_AF (USART1_TX); - MAP_AF_PU (USART1_RX); - - - usart_set_baudrate (USART1, 57600); - usart_set_databits (USART1, 8); - usart_set_stopbits (USART1, USART_STOPBITS_1); - usart_set_parity (USART1, USART_PARITY_NONE); - usart_set_flow_control (USART1, USART_FLOWCONTROL_NONE); - usart_set_mode (USART1, USART_MODE_TX_RX); - - USART_CR1 (USART1) |= USART_CR1_RXNEIE; - - usart_enable (USART1); - - - ring_init (&rx3_ring, rx3_ring_buf, sizeof (rx3_ring_buf)); - ring_init (&tx3_ring, tx3_ring_buf, sizeof (tx3_ring_buf)); - - - nvic_enable_irq (NVIC_USART3_IRQ); - - MAP_AF (USART3_TX); - MAP_AF_PU (USART3_RX); - - usart_set_baudrate (USART3, 9600); - usart_set_databits (USART3, 8); - usart_set_stopbits (USART3, USART_STOPBITS_1); - usart_set_parity (USART3, USART_PARITY_NONE); - usart_set_flow_control (USART3, USART_FLOWCONTROL_NONE); - usart_set_mode (USART3, USART_MODE_TX_RX); - - USART_CR1 (USART3) |= USART_CR1_RXNEIE; - - usart_enable (USART3); -} diff --git a/stm32/app/util.c b/stm32/app/util.c deleted file mode 100644 index bf65510..0000000 --- a/stm32/app/util.c +++ /dev/null @@ -1,14 +0,0 @@ -#include "project.h" - -char int_to_hex_char (int a) -{ - if (a < 0) return '?'; - - if (a < 0xa) return '0' + a; - - if (a < 0x10) return '7' + a; - - return '?'; -} - - diff --git a/stm32/docs/pinout.txt b/stm32/docs/pinout.txt deleted file mode 100644 index 03abe35..0000000 --- a/stm32/docs/pinout.txt +++ /dev/null @@ -1,24 +0,0 @@ - -A0 <- pressure sensor -A2 <-> one wire bus -A11 -> yellow LED (OD) -A12 -> green LED 1 (OD) -A15 -> green LED 2 (OD) - -B1 -> PIC RESET -B3 -> red LED (OD) -B6 <- PIC B6 LED_C (from A0, OT from thermostat) -B7 <- PIC B7 LED_D (from A1, OT from boiler) -B8 -> PIC A6 GPIO_A (to A3, OT to boiler) -B9 -> PIC A7 GPIO_B (to A4, OT to thermostat) -B10 -> PIC RX -B11 <- PIX TX - -C13 -> onboard LED (OD) - -A2 -> MR3020 TX -A3 <- MR4020 RX -RST <- MR3020 GPIO5 - - - diff --git a/stm32/docs/pm0056.pdf b/stm32/docs/pm0056.pdf deleted file mode 100644 index 659c9de..0000000 Binary files a/stm32/docs/pm0056.pdf and /dev/null differ diff --git a/stm32/docs/rm0008.pdf b/stm32/docs/rm0008.pdf deleted file mode 100644 index 8f3d0e1..0000000 Binary files a/stm32/docs/rm0008.pdf and /dev/null differ diff --git a/stm32/docs/stm32f103c8.pdf b/stm32/docs/stm32f103c8.pdf deleted file mode 100644 index 2723cee..0000000 Binary files a/stm32/docs/stm32f103c8.pdf and /dev/null differ diff --git a/stm32/docs/stm32f103c8t6_pinout_voltage01.png b/stm32/docs/stm32f103c8t6_pinout_voltage01.png deleted file mode 100644 index 2c22a74..0000000 Binary files a/stm32/docs/stm32f103c8t6_pinout_voltage01.png and /dev/null differ diff --git a/stm32/libopencm3 b/stm32/libopencm3 deleted file mode 160000 index 5c73d60..0000000 --- a/stm32/libopencm3 +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5c73d601763dd140bff020be8d6d06f03c2bea78 diff --git a/tasmota-config/configure-radiators b/tasmota-config/configure-radiators new file mode 100755 index 0000000..488e7e3 --- /dev/null +++ b/tasmota-config/configure-radiators @@ -0,0 +1,74 @@ +#!/bin/bash + +# Adapted from https://tasmota.github.io/docs/Rules/#simple-thermostat-example + +# Button give 30 mins of hot. + +# Low setpoint is var1 +# High setpoint is var2 +# Override is in var3 +# we store current relay state in var4 +# and use var5 for computing delta + +# Timer1 is used as a WDT incase sensor fails +# + +M=10.32.139.1 + + +set -x + +for T in laundry_radiator ; do #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" + sleep 3 + mosquitto_pub -h "${M}" -t "cmnd/${T}/Backlog" -m "Setoption36 0; Setoption65 1; Setoption1 1; SensorRetain 1; PowerRetain 1" + sleep 3 + +## + + read -r -d '' R << EOF + 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 +EOF + R="$(echo $R)" + + mosquitto_pub -h "${M}" -t "cmnd/${T}/Rule1" -m "${R}" + sleep 5 + +## + + 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 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 +EOF + R="$(echo $R)" + + mosquitto_pub -h "${M}" -t "cmnd/${T}/Rule2" -m "${R}" + sleep 5 + +## + + read -r -d '' R << 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 +EOF + R="$(echo $R)" + + mosquitto_pub -h "${M}" -t "cmnd/${T}/Rule3" -m "${R}" + sleep 5 + +## + + mosquitto_pub -h "${M}" -t "cmnd/${T}/Restart" -m "1" + +done -- cgit v1.2.3