#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(); }