Brewhouse/src/FermController/FermController.ino

225 lines
6.4 KiB
C++

//#include <PubSubClient.h>
#include <ArduinoJson.h>
#include <IPAddress.h>
#include <DallasTemperature.h>
// My Libraries
#include <secrets.h>
#include <Global.h>
#include <Communicator.h>
#include <Device.h>
OneWire oneWire(ONE_WIRE_BUS);
DallasTemperature sensors(&oneWire);
byte glycol_ds18b20[8] = {0x28,0xFF,0x64,0x0E,0x7F,0x57,0x09,0x66};
byte fermA_ds18b20[8] = {0x28,0xFF,0x64,0x0E,0x7F,0x57,0x09,0x66};
byte fermB_ds18b20[8] = {0x28,0xFF,0x64,0x0E,0x7F,0x57,0x09,0x66};
String chiller_state = "idle";
int tank_setpoint = 28;
WiFiClient net;
ControlDevice DEVICE_LIST[3];
int TOTAL_DEVICES = 0;
auto chipid = String(ESP.getChipId(), HEX);
Communicator hass_comm = Communicator(net);
unsigned long lastMillis;
unsigned long currentMillis;
void mqttCallback(char *topic, byte *payload, unsigned int length) {
payload[length] = '\0';
for (int i=0;i<TOTAL_DEVICES;i++) {
if (strcmp((char *)topic, (DEVICE_LIST[i].mode.CmdTopic).c_str() ) == 0){
DEVICE_LIST[i].mode.Setting = (char*)payload;
if (DEVICE_LIST[i].mode.Setting == "auto") {
DEVICE_LIST[i].swing_state = "off";
hass_comm.publish_data(DEVICE_LIST[i].swing_topic, "on");
} else {
DEVICE_LIST[i].swing_state = "on";
hass_comm.publish_data(DEVICE_LIST[i].swing_topic, "off");
}
hass_comm.publish_data(DEVICE_LIST[i].mode.StateTopic, DEVICE_LIST[i].mode.Setting);
break;
}
int setpoint_count;
bool dual_mode = DEVICE_LIST[i].dual;
if (dual_mode) {
setpoint_count = 3;
} else {
setpoint_count = 1;
}
for (int j=0;j<setpoint_count;j++) {
String thistopic = DEVICE_LIST[i].setpoints[j].CmdTopic;
if (strcmp((char *)topic, thistopic.c_str()) == 0){
DEVICE_LIST[i].setpoints[j].Setting = atoi((char *)payload);
hass_comm.publish_data(DEVICE_LIST[i].setpoints[j].StateTopic, String(DEVICE_LIST[i].setpoints[j].Setting));
break;
}
}
}
}
void merge(JsonObject dest, JsonObjectConst src) {
for (auto kvp : src) {
dest[kvp.key()] = kvp.value();
}
}
/* Setup MQTT discovery for chiller tank control.
*
* Using climate device.
*/
void climateDevice(String name, byte ds18b20[], bool multimode=false) {
int setpoint_count = 0;
String name_slug = slugify(name);
String config_topic = "homeassistant/climate/" + name_slug + "_" + chipid + "/config";
String topic_root = "brewhouse/" + name_slug + "/";
ControlDevice device;
device.dual = false;
device.name = name;
device.topic_root = topic_root;
for (int i=0;i<8;i++) {
device.temp_sensor[i] = ds18b20[i];
}
StaticJsonDocument<1536> entity;
entity["uniq_id"] = chipid + "_" + name_slug;
entity["name"] = name;
entity["value_template"] = "{{ value }}";
entity["temp_unit"] = "F";
entity["max_temp"] = 100;
entity["min_temp"] = 0;
entity["swing_mode_stat_t"] = topic_root + SWING_STATE;
device.swing_topic = topic_root + SWING_STATE;
entity["act_t"] = topic_root + ACTION_TPC;
entity["act_tpl"] = "{{ value }}";
device.action_topic = topic_root + ACTION_TPC;
// Mode setup
entity["mode_cmd_t"] = topic_root + MODE_SET;
Mode mode = {topic_root + MODE_SET, topic_root + MODE_STATE, "off"};
device.mode = mode;
entity["mode_cmd_tpl"] = "{{ value }}";
entity["mode_stat_t"] = topic_root + MODE_STATE;
JsonArray modes = entity.createNestedArray("modes");
modes.add("off");
modes.add("cool");
entity["temp_cmd_t"] = topic_root + TEMP_SET;
SetPoint temp = {topic_root + TEMP_SET,topic_root + TEMP_STATE,70};
device.setpoints[setpoint_count] = temp;
setpoint_count++;
entity["temp_cmd_tpl"] = "{{ value }}";
entity["temp_stat_t"] = topic_root + TEMP_STATE;
entity["curr_temp_t"] = topic_root + TEMP_CURRENT;
entity["curr_temp_tpl"] = "{{ value }}";
device.current_temp_topic= topic_root + TEMP_CURRENT;
if (multimode == true) {
device.dual = true;
entity["temp_hi_cmd_t"] = topic_root + TEMP_HI_SET;
SetPoint temp_hi = {topic_root + TEMP_HI_SET, topic_root + TEMP_HI_STATE, 70};
device.setpoints[setpoint_count] = temp_hi;
setpoint_count++;
entity["temp_hi_cmd_tpl"] = "{{ value }}";
entity["temp_hi_stat_t"] = topic_root + TEMP_HI_STATE;
entity["temp_lo_cmd_t"] = topic_root + TEMP_LO_SET;
SetPoint temp_lo = {topic_root + TEMP_LO_SET, topic_root + TEMP_LO_STATE, 70};
device.setpoints[setpoint_count] = temp_lo;
setpoint_count++;
entity["temp_lo_cmd_tpl"] = "{{ value }}";
entity["temp_lo_stat_t"] = topic_root + TEMP_LO_STATE;
modes.add("heat");
modes.add("auto");
}
JsonObject dev = entity.createNestedObject("dev");
dev["name"] = DEVICE_NAME;
dev["mdl"] = DEVICE_MDL;
dev["sw"] = DEVICE_SW;
dev["mf"] = DEVICE_MF;
JsonArray ids = dev.createNestedArray("ids");
ids.add(chipid);
//dev["ids"] = "[\"" + chipid +"\"]";
DEVICE_LIST[TOTAL_DEVICES] = device;
TOTAL_DEVICES++;
hass_comm.mqtt_discovery(config_topic, entity);
}
void SendUpdate() {
int setpoint_count = 0;
float device_temp;
for (int i=0;i<TOTAL_DEVICES;i++) {
device_temp = UpdateTemperature(&sensors, DEVICE_LIST[i].temp_sensor);
hass_comm.publish_data(DEVICE_LIST[i].current_temp_topic, String(device_temp));
hass_comm.publish_data(DEVICE_LIST[i].mode.StateTopic, DEVICE_LIST[i].mode.Setting);
hass_comm.publish_data(DEVICE_LIST[i].action_topic, ACTION_COOLING);
bool dual_mode = DEVICE_LIST[i].dual;
if (dual_mode) {
setpoint_count = 3;
} else {
setpoint_count = 1;
}
for (int j=0;j<setpoint_count;j++) {
hass_comm.publish_data(DEVICE_LIST[i].setpoints[j].StateTopic, String(DEVICE_LIST[i].setpoints[j].Setting));
}
}
}
void setup() {
hass_comm.connectCallback(&mqttCallback);
const char* ssid = WIFI_SSID;
const char* password = WIFI_PASSWORD;
sensors.begin();
sensors.setResolution(glycol_ds18b20, 9);
Serial.begin(115200);
WiFi.begin(ssid, password);
if (WiFi.waitForConnectResult() != WL_CONNECTED) {
Serial.println("WiFi Failed!");
return;
}
Serial.println();
Serial.print("IP Address: ");
Serial.println(WiFi.localIP());
climateDevice("Coolant Tank", glycol_ds18b20);
climateDevice("Fermenter 1", fermA_ds18b20,true);
climateDevice("Fermenter 2", fermB_ds18b20,true);
SendUpdate();
lastMillis = millis();
}
void loop() {
currentMillis = millis();
if (currentMillis - lastMillis >= 10000) {
sensors.requestTemperatures();
SendUpdate();
lastMillis = currentMillis;
}
delay(100);
hass_comm.loop();
}