summaryrefslogtreecommitdiffstats
path: root/boiler-monster/stm32/app/ot.c
diff options
context:
space:
mode:
Diffstat (limited to 'boiler-monster/stm32/app/ot.c')
-rw-r--r--boiler-monster/stm32/app/ot.c452
1 files changed, 452 insertions, 0 deletions
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",
+ "<Read ACK",
+ "<Write ACK",
+ "<Data Invalid",
+ "<Unknown DataID"
+};
+
+static void ot_debug (char *who, uint8_t *msg, char *what)
+{
+ unsigned type = (msg[0] >> 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();
+}