summaryrefslogtreecommitdiffstats
path: root/radiator-plc
diff options
context:
space:
mode:
authorfishsoupisgood <github@madingley.org>2020-09-09 11:53:37 +0100
committerfishsoupisgood <github@madingley.org>2020-09-09 11:53:37 +0100
commit9d87c925a9eaa4fc256be3173c14a20d1469472d (patch)
tree50d63f87a47a0eac3f5b8058850184bcd4e6ee95 /radiator-plc
parentdafd8cf2fdcdd637cc06f760d318cf8391b1a294 (diff)
downloadheating-9d87c925a9eaa4fc256be3173c14a20d1469472d.tar.gz
heating-9d87c925a9eaa4fc256be3173c14a20d1469472d.tar.bz2
heating-9d87c925a9eaa4fc256be3173c14a20d1469472d.zip
everything, mostly, working
Diffstat (limited to 'radiator-plc')
-rw-r--r--radiator-plc/DOCS/DS18B20.pdfbin0 -> 392186 bytes
-rw-r--r--radiator-plc/DOCS/plc_notes.txt93
-rw-r--r--radiator-plc/DOCS/radiator-numbering.txt8
-rw-r--r--radiator-plc/DOCS/valve_01213210.pdfbin0 -> 341645 bytes
-rw-r--r--radiator-plc/DOCS/valve_wiring.txt15
-rw-r--r--radiator-plc/mt300n-v2/.gitignore1
-rw-r--r--radiator-plc/mt300n-v2/Makefile6
-rwxr-xr-xradiator-plc/mt300n-v2/usr/bin/mqtt_if.pl76
-rwxr-xr-xradiator-plc/mt300n-v2/usr/bin/run_mqtt_if6
-rw-r--r--radiator-plc/stm32/.gitignore15
-rw-r--r--radiator-plc/stm32/Makefile8
-rw-r--r--radiator-plc/stm32/Makefile.include44
-rw-r--r--radiator-plc/stm32/Makefile.rules261
-rw-r--r--radiator-plc/stm32/app/1wire.c384
-rw-r--r--radiator-plc/stm32/app/1wire.h9
-rw-r--r--radiator-plc/stm32/app/Makefile59
-rw-r--r--radiator-plc/stm32/app/control.c114
-rw-r--r--radiator-plc/stm32/app/ds18b20.c128
-rw-r--r--radiator-plc/stm32/app/gdb.script2
-rw-r--r--radiator-plc/stm32/app/hexdump.c57
-rw-r--r--radiator-plc/stm32/app/inputs.c26
-rw-r--r--radiator-plc/stm32/app/led.c53
-rw-r--r--radiator-plc/stm32/app/logic.c158
-rw-r--r--radiator-plc/stm32/app/main.c150
-rw-r--r--radiator-plc/stm32/app/outputs.c47
-rw-r--r--radiator-plc/stm32/app/pins.h53
-rw-r--r--radiator-plc/stm32/app/project.h29
-rw-r--r--radiator-plc/stm32/app/prototypes.h80
-rw-r--r--radiator-plc/stm32/app/radiator-plc.ld38
-rw-r--r--radiator-plc/stm32/app/rain.c100
-rw-r--r--radiator-plc/stm32/app/ring.c69
-rw-r--r--radiator-plc/stm32/app/ring.h6
-rw-r--r--radiator-plc/stm32/app/ticker.c97
-rw-r--r--radiator-plc/stm32/app/usart.c149
-rw-r--r--radiator-plc/stm32/app/watchdog.c16
m---------radiator-plc/stm32/libopencm30
36 files changed, 2357 insertions, 0 deletions
diff --git a/radiator-plc/DOCS/DS18B20.pdf b/radiator-plc/DOCS/DS18B20.pdf
new file mode 100644
index 0000000..52dc259
--- /dev/null
+++ b/radiator-plc/DOCS/DS18B20.pdf
Binary files 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
--- /dev/null
+++ b/radiator-plc/DOCS/valve_01213210.pdf
Binary files 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 <uwe@hermann-uwe.de>
+## Copyright (C) 2010 Piotr Esden-Tempski <piotr@esden.net>
+##
+## 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 <http://www.gnu.org/licenses/>.
+##
+
+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 <uwe@hermann-uwe.de>
+## Copyright (C) 2010 Piotr Esden-Tempski <piotr@esden.net>
+## Copyright (C) 2013 Frantisek Burian <BuFran@seznam.cz>
+##
+## 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 <http://www.gnu.org/licenses/>.
+##
+
+# 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 <uwe@hermann-uwe.de>
+##
+## 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 <http://www.gnu.org/licenses/>.
+##
+
+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 <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <libopencm3/stm32/rcc.h>
+#include <libopencm3/stm32/gpio.h>
+#include <libopencm3/stm32/usart.h>
+#include <libopencm3/stm32/iwdg.h>
+#include <libopencm3/stm32/dma.h>
+#include <libopencm3/stm32/i2c.h>
+#include <libopencm3/cm3/systick.h>
+#include <libopencm3/cm3/nvic.h>
+#include <libopencm3/cm3/cortex.h>
+#include <libopencm3/cm3/scs.h>
+
+
+
+#include <stdio.h>
+#include <errno.h>
+
+#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 <uwe@hermann-uwe.de>
+ *
+ * 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 <http://www.gnu.org/licenses/>.
+ */
+
+/* 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
+Subproject 5c73d601763dd140bff020be8d6d06f03c2bea7