diff --git a/.gitignore b/.gitignore index e9e2ef2..ac720c6 100755 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,5 @@ .DS_Store -bin/*/ +bin/* *build-* *build* *~ @@ -9,4 +9,5 @@ doc/ debug.txt *.geany *.tags +tags *.bin diff --git a/MakefileGlobal.mk b/MakefileGlobal.mk index 5de1361..4e73995 100644 --- a/MakefileGlobal.mk +++ b/MakefileGlobal.mk @@ -17,6 +17,8 @@ TARGET_DIR := $(BASE_DIR)/$(subst :,.,build/$(FQBN)) BIN_DIR := $(BASE_DIR)/bin SRC := $(wildcard $(SKETCH_DIR)/*.ino) HDRS := $(wildcard $(SKETCH_DIR)/*.h) +LIBH := $(wildcard $(BASE_DIR)/lib/**/*.h) +LIBS := $(LIBH) $(wildcard $(BASE_DIR)/lib/**/*.cpp) BIN := $(notdir $(SRC)).bin $(info FQBN is [${FQBN}]) @@ -36,7 +38,7 @@ all: $(BIN_DIR)/$(BIN) upload compile: $(BIN_DIR)/$(BIN) upload: $(BIN_DIR)/$(BIN) - $(ESPTOOL) -v -cd nodemcu -b 115200 -p $(MONITOR_PORT) -ca 0x00000 -cf $(TARGET_DIR)/$(SKETCH).bin + $(ESPTOOL) --port $(MONITOR_PORT) --chip esp8266 --baud 460800 write_flash -fm dio -fs 16MB 0x0 $(TARGET_DIR)/$(SKETCH).bin ota: $(BIN_DIR)/$(BIN) $(ESPOTA) -d -r -i $(ESP_IP) -I $(OTA_SERVER) -p 8266 -P 8266 -a $(OTA_PASSWD) -f $(TARGET_DIR)/$(SKETCH).bin @@ -59,7 +61,7 @@ new-library: .PHONY: all compile test library upload ota clean monitor -$(TARGET_DIR)/$(BIN): $(SRC) $(HDRS) +$(TARGET_DIR)/$(BIN): $(SRC) $(HDRS) $(LIBS) @ echo $(FQBN) @ mkdir -p $(TARGET_DIR) @@ -73,4 +75,4 @@ $(TARGET_DIR)/$(BIN): $(SRC) $(HDRS) $(BIN_DIR)/$(BIN): $(TARGET_DIR)/$(BIN) @ echo "$(BIN) $(BIN_DIR)/" - @ mv $(TARGET_DIR)/$(BIN) $(BIN_DIR)/ + @ cp $(TARGET_DIR)/$(BIN) $(BIN_DIR)/ diff --git a/bin/FermController.ino.bin b/bin/FermController.ino.bin deleted file mode 100644 index 9557f2b..0000000 Binary files a/bin/FermController.ino.bin and /dev/null differ diff --git a/lib/Communicator/communicator.cpp b/lib/Communicator/Communicator.cpp similarity index 69% rename from lib/Communicator/communicator.cpp rename to lib/Communicator/Communicator.cpp index 6877c53..20aa5ee 100644 --- a/lib/Communicator/communicator.cpp +++ b/lib/Communicator/Communicator.cpp @@ -1,11 +1,20 @@ -#include "communicator.h" +#include "Communicator.h" -Communicator::Communicator(WiFiClient& network, void (*cmd)(char *topic, byte *payload, unsigned int length)) { +Communicator::Communicator(WiFiClient& network, CALLBACK_SIGNATURE) { this->_net = network; - this->mqttCallback = cmd; + this->mqttCallback = mqttCallback; this->_mqtt_client.setBufferSize(1536); } +Communicator::Communicator(WiFiClient& network) { + this->_net = network; + this->_mqtt_client.setBufferSize(1536); +} + +void Communicator::connectCallback(CALLBACK_SIGNATURE) { + this->mqttCallback = mqttCallback; +} + void Communicator::loop() { _mqtt_client.loop(); } @@ -80,6 +89,11 @@ bool Communicator::ConnectMQTT(const String &server, const String &name, const S } +void Communicator::Subscribe(const char* topic) { + Serial.println("Subscribe to: " + String(topic)); + _mqtt_client.subscribe(topic); +} + void Communicator::mqtt_discovery(const String topic, StaticJsonDocument<1536> &entity) { String payload; serializeJson(entity, payload); @@ -89,21 +103,31 @@ void Communicator::mqtt_discovery(const String topic, StaticJsonDocument<1536> & { Serial.print("Sending discovery payload to "); Serial.println(topic); - Serial.println(""); Serial.println(payload); - Serial.println(""); - + _mqtt_client.publish(topic.c_str(), payload.c_str(), false); - if (entity.containsKey("mode_cmd_t")) _mqtt_client.subscribe(entity["mode_cmd_t"]); - if (entity.containsKey("temp_cmd_t")) _mqtt_client.subscribe(entity["temp_cmd_t"]); - if (entity.containsKey("temp_hi_cmd_t")) _mqtt_client.subscribe(entity["temp_hi_cmd_t"]); - if (entity.containsKey("temp_lo_cmd_t")) _mqtt_client.subscribe(entity["temp_lo_cmd_t"]); + const char* mode = entity["mode_cmd_t"]; + if (mode) { + Subscribe(mode); + } + const char* temp = entity["temp_cmd_t"]; + if (temp) { + Subscribe(temp); + } + const char* temp_hi = entity["temp_hi_cmd_t"]; + if (temp_hi) { + Subscribe(temp_hi); + } + const char* temp_lo = entity["temp_lo_cmd_t"]; + if (temp_lo) { + Subscribe(temp_lo); + } _mqtt_client.loop(); } } - + void Communicator::publish_data(String topic, String value) { - _mqtt_client.publish(topic.c_str(), value.c_str(), false); + _mqtt_client.publish(topic.c_str(), value.c_str(), true); } diff --git a/lib/Communicator/Communicator.h b/lib/Communicator/Communicator.h new file mode 100644 index 0000000..12c47e2 --- /dev/null +++ b/lib/Communicator/Communicator.h @@ -0,0 +1,31 @@ +#ifndef MyClass_h +#define MyClass_h + +#include +#include +#include + +#include +#include + +#define CALLBACK_SIGNATURE void (*mqttCallback)(char *topic, byte *payload, unsigned int length) + +class Communicator { +public: + Communicator(WiFiClient&); + Communicator(WiFiClient&, CALLBACK_SIGNATURE); + + CALLBACK_SIGNATURE; + void connectCallback(CALLBACK_SIGNATURE); + bool ConnectMQTT(const String&, const String&, const String&, const String&); + void mqtt_discovery(const String, StaticJsonDocument<1536>&); + void publish_data(String topic, String value); + void publish_data(String topic, int value); + void Subscribe(const char* topic); + void loop(); + +private: + PubSubClient _mqtt_client; + WiFiClient _net; +}; +#endif diff --git a/lib/Communicator/communicator.h b/lib/Communicator/communicator.h deleted file mode 100644 index de83744..0000000 --- a/lib/Communicator/communicator.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef MyClass_h -#define MyClass_h - -#include -#include -#include - -#include "secrets.h" - - -class Communicator { -public: - Communicator(WiFiClient&, void (*mqttCallback)(char*, byte*, unsigned int)); - - void (*mqttCallback)(char*, byte*, unsigned int); - bool ConnectMQTT(const String&, const String&, const String&, const String&); - void mqtt_discovery(const String, StaticJsonDocument<1536>&); - void publish_data(String, String); - - void loop(); -private: - PubSubClient _mqtt_client; - WiFiClient _net; -}; -#endif diff --git a/lib/Device/Device.cpp b/lib/Device/Device.cpp deleted file mode 100644 index ea0008e..0000000 --- a/lib/Device/Device.cpp +++ /dev/null @@ -1,358 +0,0 @@ - -#include "Device.h" - -Device::Device(const uint8_t _pin_cool, const uint8_t _pin_heat, const uint8_t _pin_wire) { - this->_pin_cool = _pin_cool; - this->_pin_heat = _pin_heat; - this->_pin_wire = _pin_wire; -} - -void Device::CoolSet(bool set_state) { - // Set pin state - if (set_state) { - Serial.println("Starting cooling."); - strcpy(_curr_action, ACTION_COOLING); - SendState(ACTION_TPC, _curr_action); - digitalWrite(_pin_heat, LOW); // Just to be sure. - digitalWrite(_pin_cool, HIGH); - } else { - Serial.println("Stopping cooling."); - strcpy(_curr_action, ACTION_IDLE); - SendState(ACTION_TPC, _curr_action); - digitalWrite(_pin_cool, LOW); - digitalWrite(_pin_heat, LOW); - } -} - -void Device::HeatSet(bool set_state) { - if (set_state) { - Serial.println("Starting heat."); - strcpy(_curr_action, ACTION_HEATING); - SendState(ACTION_TPC, _curr_action); - digitalWrite(_pin_cool, LOW); // Just to be sure. - digitalWrite(_pin_heat, HIGH); - } else { - Serial.println("Stopping heat."); - strcpy(_curr_action, ACTION_IDLE); - SendState(ACTION_TPC, _curr_action); - digitalWrite(_pin_cool, LOW); - digitalWrite(_pin_heat, LOW); - } -} - -void Device::Hyst(float new_value){ - _hyst = new_value; -} - -float Device::Hyst(){ - return _hyst; -} - -void Device::Update(){ - float currentMillis = millis(); - - //Read temp sensor. - if (currentMillis - _lastSensor >= _sensor_period) { - //Read temperature from DS18b20 - float tempC = _sensors.getTempC(_ds_serial); - if (tempC == DEVICE_DISCONNECTED_C) { - Serial.println("Error: Could not read temperature data"); - } else { - _curr_temp = DallasTemperature::toFahrenheit(tempC); - } - } - - // Some helpful variables. - bool heating = _curr_action==ACTION_HEATING; - bool cooling = _curr_action==ACTION_COOLING; - bool running = (heating || cooling); - - // Adjust cool/heat on or off - if (_mode == "cool"){ - if (_curr_temp > _temp + _hyst && !cooling) { - CoolSet(true); - } else if (_curr_temp <= _temp && cooling) { - CoolSet(false); - } - } else if (_mode == "heat"){ - if (_curr_temp < _temp - _hyst && !heating) { - HeatSet(true); - } else if (_curr_temp >= _temp && heating) { - HeatSet(false); - } - } else if (_mode == "auto"){ - if ((_curr_temp < _temp_lo - _hyst) && !heating) { - HeatSet(true); - } else if ((_curr_temp > _temp_hi + _hyst) && !cooling) { - CoolSet(true); - } else if (running && (_curr_temp >= _temp_lo) && (_curr_temp <= _temp_hi)) { - if (heating) HeatSet(false); - if (cooling) CoolSet(false); - } - } else { - // IS OFF - if (heating) HeatSet(false); - if (cooling) CoolSet(false); - } - - //Send Data to broker - if (currentMillis - _lastSend >= _send_period) { - // Time's up, send data - char temp[7]; - dtostrf(_curr_temp, 6, 2, temp); - SendState(TEMP_CURRENT, temp); - _lastSend = currentMillis; - } - - _mqtt_client.loop(); -} - -void Device::AttachNet(WiFiClient &network) { - this->_net = network; - this->_mqtt_client.setClient(_net); - this->_mqtt_client.setBufferSize(DOC_SIZE); -} - -void Device::AttachSensor(DallasTemperature &sensors, uint8_t serial[8]){ - _sensors = sensors; - _ds_serial = serial; -} - -//void Device::topicRoot(const String &root) { _device_prefix = root; } -//byte* Device::topicRoot() { return _device_prefix; } - -void Device::SendConfig(char* broker, char* name, String &chipid, bool multimode) { - String name_slug = slugify(name); - String CMD_TPL = "{{ value }}"; - String STAT_TPL = "{{ value_json }}"; - - _topic_root = ROOT + name_slug; - - String _config_topic = CONFIG_ROOT + name_slug + "_" + chipid + "/config"; - - StaticJsonDocument payload_doc; - payload_doc["uniq_id"] = chipid + "_" + name_slug; - payload_doc["name"] = name; - payload_doc["temp_unit"] = "F"; - payload_doc["max_temp"] = _max_temp; - payload_doc["min_temp"] = _min_temp; - payload_doc["initial"] = _init_temp; - - // Action Topic - payload_doc["action_topic"] = _topic_root + ACTION_TPC; - payload_doc["action_template"] = STAT_TPL; - - // Mode setup - payload_doc["mode_cmd_t"] = _topic_root + MODE_SET; - payload_doc["mode_cmd_tpl"] = CMD_TPL; - payload_doc["mode_stat_t"] = _topic_root + MODE_STATE; - payload_doc["mode_stat_tpl"] = STAT_TPL; - - JsonArray modes = payload_doc.createNestedArray("modes"); - modes.add("off"); - modes.add("cool"); - - payload_doc["temp_cmd_t"] = _topic_root + TEMP_SET; - payload_doc["temp_cmd_tpl"] = CMD_TPL; - payload_doc["temp_stat_t"] = _topic_root + TEMP_STATE; - payload_doc["temp_stat_tpl"] = STAT_TPL; - - payload_doc["curr_temp_t"] = _topic_root + TEMP_CURRENT; - payload_doc["curr_temp_tpl"] = CMD_TPL; - - if (multimode) { - payload_doc["temp_hi_cmd_t"] = _topic_root + TEMP_HI_SET; - payload_doc["temp_hi_cmd_tpl"] = CMD_TPL; - payload_doc["temp_hi_stat_t"] = _topic_root + TEMP_HI_STATE; - payload_doc["temp_hi_stat_tpl"] = STAT_TPL; - - payload_doc["temp_lo_cmd_t"] = _topic_root + TEMP_LO_SET; - payload_doc["temp_lo_cmd_tpl"] = CMD_TPL; - payload_doc["temp_lo_stat_t"] = _topic_root + TEMP_LO_STATE; - payload_doc["temp_lo_stat_tpl"] = STAT_TPL; - - modes.add("heat"); - modes.add("auto"); - } - - // Attach Device - JsonObject dev = payload_doc.createNestedObject("dev"); - dev["name"] = DEVICE_NAME; - dev["mdl"] = DEVICE_MDL; - dev["sw"] = String(version); - dev["mf"] = DEVICE_MF; - JsonArray ids = dev.createNestedArray("ids"); - ids.add(chipid); - - String payload; - serializeJson(payload_doc, payload); - - - bool response = ConnectMQTT(broker, MQTT_NAME, MQTT_USER, MQTT_PASSWORD); - if (response) { - _mqtt_client.publish(_config_topic.c_str(), payload.c_str(), MSG_RETAIN); - _mqtt_client.loop(); - } - - _mqtt_client.subscribe((_topic_root + MODE_SET).c_str()); - _mqtt_client.subscribe((_topic_root + TEMP_SET).c_str()); - if (multimode) { - _mqtt_client.subscribe((_topic_root + TEMP_HI_SET).c_str()); - _mqtt_client.subscribe((_topic_root + TEMP_LO_SET).c_str()); - } - - _mqtt_client.loop(); -} - -void Device::_Temp(byte value){ - Serial.print("Set Temp"); - _temp = value; - SendState(TEMP_STATE, (char*)value); -} -void Device::_Mode(char* value){ - Serial.print("Set Mode"); - strcpy(_mode, value); - SendState(MODE_STATE, (char*)value); -} -void Device::_TempHi(byte value){ - Serial.print("Set High Temp"); - _temp_hi = value; - SendState(TEMP_HI_STATE, (char*)value); -} -void Device::_TempLo(byte value){ - Serial.print("Set Low Temp"); - _temp_lo = value; - SendState(TEMP_LO_STATE, (char*)value); -} - -void Device::SendState(String suffix, String payload){ - String topic = _topic_root + suffix; - - _mqtt_client.publish(topic.c_str(), payload.c_str(), MSG_RETAIN); -} - -/** Callback function for MQTT client. - Looks up a command function based on topic and executes it. - - @param topic - @param payload - @param length -*/ -void Device::_mqttCallback(char *topic, uint8_t *payload, unsigned int length) { - char data [16] = {'\0'}; - - // Remove root from the incoming topic. - char suffix[100] = topic[_topic_root.length()]; - Serial.print("Incoming topic -> "); - Serial.print(suffix); - Serial.print(": "); - int i; - - for (i; i < length; i++) - { - data[i] = ((char)payload[i]); - Serial.print(data[i]); - } - data[i] = '\0'; - - switch (suffix) { - - case MODE_SET: - _Mode(data); - break; - - case TEMP_SET: - _Temp(atoi(data)); - break; - - case TEMP_LO_SET: - _TempLo(atoi(data)); - break; - - case TEMP_HI_SET: - _TempHi(atoi(data)); - break; - - default: - Serial.println("Command function not found for " + String(topic)); - } - -} - -/** Connect to MQTT broker. - - @param server IP address string of the server. - @param name the name used for this connection - @param user MQTT broker username - @param password MQTT broker password - @return boolean indicating success or failure of connection. -*/ -bool Device::ConnectMQTT(const String &server, const String &name, const String &user, const String &password) { - - _mqtt_client.setServer((server.c_str(), 1883); - _mqtt_client.setCallback(&Device::_mqttCallback); - - byte i = 0; - - if (_mqtt_client.connected()) return true; - - while (!_mqtt_client.connected() && (i < 3)) { - Serial.println("Attempt MQTT Connection."); - boolean ret; - ret = _mqtt_client.connect(name.c_str(), user.c_str(), password.c_str()); - if (ret) { - Serial.println("Connected to MQTT"); - return true; - - } else { - int Status = _mqtt_client.state(); - - switch (Status) - { - case -4: - Serial.println(F("Connection timeout")); - break; - - case -3: - Serial.println(F("Connection lost")); - break; - - case -2: - Serial.println(F("Connect failed")); - break; - - case -1: - Serial.println(F("Disconnected")); - break; - - case 1: - Serial.println(F("Bad protocol")); - break; - - case 2: - Serial.println(F("Bad client ID")); - break; - - case 3: - Serial.println(F("Unavailable")); - break; - - case 4: - Serial.println(F("Bad credentials")); - break; - - case 5: - Serial.println(F("Unauthorized")); - break; - } - - } - - Serial.print("."); - i++; - delay(5000); - } - - return false; - -} diff --git a/lib/Device/Device.h b/lib/Device/Device.h index 6983304..4456cd3 100644 --- a/lib/Device/Device.h +++ b/lib/Device/Device.h @@ -4,7 +4,7 @@ #include #include #include -#include +#include #include #include @@ -14,6 +14,8 @@ const char version[] = "0.0.1"; #define ACTION_TPC "action/state" #define MODE_SET "mode/set" #define MODE_STATE "mode/state" +#define SWING_STATE "swing/state" +#define SWING_SET "swing/set" #define TEMP_SET "temp/set" #define TEMP_STATE "temp/state" #define TEMP_CURRENT "temp/current" @@ -29,17 +31,17 @@ const char version[] = "0.0.1"; #ifndef DEVICE_NAME #define DEVICE_NAME "MQTT Thermostat" -#endif +#endif #ifndef DEVICE_MDL #define DEVICE_MDL "Thermostat" -#endif +#endif #ifndef DEVICE_MF #define DEVICE_MF "" -#endif +#endif -#ifndef ROOT +#ifndef ROOT #define ROOT "thermostat/" #endif @@ -53,29 +55,129 @@ const char version[] = "0.0.1"; #define TOPIC_LIMIT 4 -//COMMAND_SIGNATURE void (*commandFunction)(uint8_t*) // https://forum.arduino.cc/t/assignment-of-function/528949/3 -//CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int) +//~ #define OFF 0 +//~ #define COOL 1 +//~ #define HEAT 2 +//~ #define AUTO 3 -const size_t DOC_SIZE = JSON_OBJECT_SIZE(29) + JSON_ARRAY_SIZE(4); +#define TEMP 0 +#define TEMP_HI 1 +#define TEMP_LO 2 + +uint8_t CHILLER_SP = 70; +uint8_t FERMA_SP = 70; +uint8_t FERMB_SP = 70; + +//~ typedef void (*ptr)(uint8_t*, uint8_t*); +//typedef ptr (*pm)(); struct CommandTopic { - void (*cmd)(byte*); + //~ ptr CmdFunc; + byte* Setting; String CmdTopic; }; +struct Mode { + String CmdTopic; + String StateTopic; + String Setting; +}; + +struct SetPoint { + String CmdTopic; + String StateTopic; + int Setting; +}; + struct ControlDevice { String name; // "Glycol Chiller" String topic_root; // "brewhouse/" - CommandTopic command_topics[TOPIC_LIMIT]; + String current_temp_topic; + byte temp_sensor[8]; + String action_topic; + String action_state; + String swing_topic; + String swing_state; + byte heat_pin; + byte cool_pin; + Mode mode; + SetPoint setpoints[3]; + bool dual; }; -void (*cmd)(byte*) CmdLookup(String command_topic, ControlDevice devices[], int device_count) { - for (int i=0;iCmdTopic == command_topic) return this_device.command_topics[j]->cmd; - } +float UpdateTemperature(DallasTemperature *sensors, DeviceAddress glycol_tank) { + // method 2 - faster + float tempC = sensors->getTempC(glycol_tank); + if(tempC == DEVICE_DISCONNECTED_C) + { + Serial.println("Error: Could not read temperature data"); + return 0; } + + return DallasTemperature::toFahrenheit(tempC); + } +bool DeviceLoop(ControlDevice &device, DallasTemperature *sensors) { + Serial.println(device.name); + + float dsTemp = UpdateTemperature(sensors, device.temp_sensor); + String initial_action = device.action_state; + + if (device.mode.Setting == "auto") { + int setHi = device.setpoints[TEMP_HI].Setting; + int setLo = device.setpoints[TEMP_LO].Setting; + + if ((setLo <= dsTemp) && (dsTemp <= setHi)) { // Within Temp Range + device.action_state = ACTION_IDLE; + digitalWrite(device.cool_pin, LOW); + digitalWrite(device.heat_pin, LOW); + } else if (dsTemp < setLo) { // Needs Heat + device.action_state = ACTION_HEATING; + digitalWrite(device.cool_pin, LOW); + digitalWrite(device.heat_pin, HIGH); + } else if (dsTemp > setHi) { // Needs Cooling + device.action_state = ACTION_COOLING; + digitalWrite(device.cool_pin, HIGH); + digitalWrite(device.heat_pin, LOW); + } + } + + int setTemp = device.setpoints[TEMP].Setting; + + if (device.mode.Setting == "cool") { + + + if (dsTemp > setTemp) { // Needs Cooling + device.action_state = ACTION_COOLING; + digitalWrite(device.cool_pin, HIGH); + digitalWrite(device.heat_pin, LOW); + } else { + device.action_state = ACTION_IDLE; + digitalWrite(device.cool_pin, LOW); + digitalWrite(device.heat_pin, LOW); + } + } + + if (device.mode.Setting == "heat") { + if (dsTemp < setTemp) { // Needs Cooling + device.action_state = ACTION_HEATING; + digitalWrite(device.cool_pin, LOW); + digitalWrite(device.heat_pin, HIGH); + } else { + device.action_state = ACTION_IDLE; + digitalWrite(device.cool_pin, LOW); + digitalWrite(device.heat_pin, LOW); + } + } + + if (device.mode.Setting == "off") { + device.action_state = ACTION_IDLE; + } + + if (initial_action != device.action_state) { + return true; + } + return false; +} #endif diff --git a/lib/Device/globals.h b/lib/Device/globals.h deleted file mode 100644 index da28ee7..0000000 --- a/lib/Device/globals.h +++ /dev/null @@ -1,11 +0,0 @@ -TOPIC_ROOT + suffix; -TOPIC_ROOT + suffix; -TOPIC_ROOT + "mode/set"; -TOPIC_ROOT + "mode/state"; -TOPIC_ROOT + "temp/set"; -TOPIC_ROOT + "temp/state"; -TOPIC_ROOT + "temp/current"; -TOPIC_ROOT + "temp_hi/set"; -TOPIC_ROOT + "temp_hi/state"; -TOPIC_ROOT + "temp_lo/set"; -TOPIC_ROOT + "temp_lo/state"; diff --git a/lib/Global/Global.h b/lib/Global/Global.h index d712593..29fea9d 100644 --- a/lib/Global/Global.h +++ b/lib/Global/Global.h @@ -7,11 +7,17 @@ source repository. #define GLOBAL_H #define DEVICE_SW "0.0.1" -auto chipid = String(ESP.getChipId(), HEX); + #define DEVICE_NAME "Glycol Chiller" #define DEVICE_MDL "Chillenator v0.1" #define DEVICE_MF "Damn Yankee Brewing" -#define FERMENTER_COUNT 2 + +#define ONE_WIRE_BUS D2 +#define GLYCOL_RELAY D3 +#define FERMA_RELAY D4 +#define FERMB_RELAY D5 + +const size_t DOC_SIZE = JSON_OBJECT_SIZE(29) + JSON_ARRAY_SIZE(4); #endif diff --git a/lib/communicator/communicator.cpp b/lib/communicator/communicator.cpp deleted file mode 100644 index dfb0bc0..0000000 --- a/lib/communicator/communicator.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include "communicator.h" - -Communicator::Communicator(WiFiClient& network, void (*cmd)(char *topic, byte *payload, unsigned int length)) { - this->_net = network; - this->mqttCallback = cmd; - this->_mqtt_client.setBufferSize(1536); -} - -void Communicator::loop() { - _mqtt_client.loop(); -} - -bool Communicator::ConnectMQTT(const String &server, const String &name, const String &user, const String &password) { - _mqtt_client.setClient(_net); - _mqtt_client.setServer(server.c_str(), 1883); - _mqtt_client.setCallback(mqttCallback); - - byte i = 0; - - if (_mqtt_client.connected()) return true; - - while (!_mqtt_client.connected() && (i < 3)) { - Serial.println("Attempt MQTT Connection."); - boolean ret; - ret = _mqtt_client.connect(name.c_str(), user.c_str(), password.c_str()); - if (ret) { - Serial.println("Connected to MQTT"); - return true; - - } else { - int Status = _mqtt_client.state(); - - switch (Status) - { - case -4: - Serial.println(F("Connection timeout")); - break; - - case -3: - Serial.println(F("Connection lost")); - break; - - case -2: - Serial.println(F("Connect failed")); - break; - - case -1: - Serial.println(F("Disconnected")); - break; - - case 1: - Serial.println(F("Bad protocol")); - break; - - case 2: - Serial.println(F("Bad client ID")); - break; - - case 3: - Serial.println(F("Unavailable")); - break; - - case 4: - Serial.println(F("Bad credentials")); - break; - - case 5: - Serial.println(F("Unauthorized")); - break; - } - - } - - Serial.print("."); - i++; - delay(5000); - } - - return false; - -} - -void Communicator::mqtt_discovery(const String topic, StaticJsonDocument<1536> &entity) { - String payload; - serializeJson(entity, payload); - - bool response = ConnectMQTT(MQTT_BROKER.toString(), MQTT_NAME, MQTT_USER, MQTT_PASSWORD); - if (response) - { - Serial.print("Sending discovery payload to "); - Serial.println(topic); - Serial.println(""); - Serial.println(payload); - Serial.println(""); - - _mqtt_client.publish(topic.c_str(), payload.c_str(), false); - - if (entity.containsKey("mode_cmd_t")) _mqtt_client.subscribe(entity["mode_cmd_t"]); - if (entity.containsKey("temp_cmd_t")) _mqtt_client.subscribe(entity["temp_cmd_t"]); - if (entity.containsKey("temp_hi_cmd_t")) _mqtt_client.subscribe(entity["temp_hi_cmd_t"]); - if (entity.containsKey("temp_lo_cmd_t")) _mqtt_client.subscribe(entity["temp_lo_cmd_t"]); - - _mqtt_client.loop(); - } -} - -void Communicator::publish_data(String topic, String value) { - _mqtt_client.publish(topic.c_str(), value.c_str(), false); -} \ No newline at end of file diff --git a/lib/communicator/communicator.h b/lib/communicator/communicator.h deleted file mode 100644 index de83744..0000000 --- a/lib/communicator/communicator.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef MyClass_h -#define MyClass_h - -#include -#include -#include - -#include "secrets.h" - - -class Communicator { -public: - Communicator(WiFiClient&, void (*mqttCallback)(char*, byte*, unsigned int)); - - void (*mqttCallback)(char*, byte*, unsigned int); - bool ConnectMQTT(const String&, const String&, const String&, const String&); - void mqtt_discovery(const String, StaticJsonDocument<1536>&); - void publish_data(String, String); - - void loop(); -private: - PubSubClient _mqtt_client; - WiFiClient _net; -}; -#endif diff --git a/lib/global/global.h b/lib/global/global.h deleted file mode 100644 index d712593..0000000 --- a/lib/global/global.h +++ /dev/null @@ -1,17 +0,0 @@ -/* This file is all the stuff I want to be -able to change without having to update the -source repository. -*/ - -#ifndef GLOBAL_H -#define GLOBAL_H - -#define DEVICE_SW "0.0.1" -auto chipid = String(ESP.getChipId(), HEX); - -#define DEVICE_NAME "Glycol Chiller" -#define DEVICE_MDL "Chillenator v0.1" -#define DEVICE_MF "Damn Yankee Brewing" -#define FERMENTER_COUNT 2 - -#endif diff --git a/lib/slowPWM/slowPWM.h b/lib/slowPWM/slowPWM.h deleted file mode 100755 index 3acfeae..0000000 --- a/lib/slowPWM/slowPWM.h +++ /dev/null @@ -1,42 +0,0 @@ -#ifndef SLOWPWM_h -#define SLOWPWM_h - -#include - -class slowPWM { - private: - byte outputPin; - unsigned long period; - unsigned long lastSwitchTime; - byte outputState; - - public: - void begin(byte pin, unsigned long per) { - outputPin = pin; - period = per; - lastSwitchTime = 0; - outputState = LOW; - pinMode(pin, OUTPUT); - Serial.println("Setup PWM"); - } - - byte compute(byte duty) { - unsigned long onTime = (duty * period) / 100; - unsigned long offTime = period - onTime; - unsigned long currentTime = millis(); - - if (duty == 0) { - outputState = LOW; - } else if (outputState == HIGH && (currentTime - lastSwitchTime >= onTime)) { - lastSwitchTime = currentTime; - outputState = LOW; - - } else if (outputState == LOW && (currentTime - lastSwitchTime >= offTime)) { - lastSwitchTime = currentTime; - outputState = HIGH; - } - - return outputState; - } -}; -#endif diff --git a/src/FermController/FermController.ino b/src/FermController/FermController.ino index dd535e7..72c3544 100644 --- a/src/FermController/FermController.ino +++ b/src/FermController/FermController.ino @@ -1,33 +1,68 @@ //#include #include #include +#include // My Libraries #include -#include -#include - -String chiller_state = "idle"; -int tank_setpoint = 28; +#include +#include +#include +OneWire oneWire(ONE_WIRE_BUS); +DallasTemperature sensors(&oneWire); WiFiClient net; +byte glycol_ds18b20[8] = {0x28,0xFF,0x64,0x0E,0x7F,0x57,0x09,0x66}; +byte fermA_ds18b20[8] = {0x28,0xFF,0x64,0x0E,0x7F,0x55,0x1C,0x55}; +byte fermB_ds18b20[8] = {0x28,0xFF,0x64,0x0E,0x71,0x86,0xF1,0xB8}; + +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) { - Serial.print("incoming: "); - Serial.println(topic); - for (unsigned int i = 0; i < length; i++) - { - Serial.print((char)payload[i]); + payload[length] = '\0'; + + for (int i=0;i 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"; + 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"; - entity["mode_stat_tpl"] = "{{ value_json }}"; + 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"; + 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["temp_stat_tpl"] = "{{ value_json }}"; - entity["curr_temp_t"] = topic_root + "temp/current"; + 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) { - entity["temp_hi_cmd_t"] = topic_root + "temp_hi/set"; + 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_hi_stat_tpl"] = "{{ value_json }}"; + entity["temp_hi_stat_t"] = topic_root + TEMP_HI_STATE; - entity["temp_lo_cmd_t"] = topic_root + "temp_lo/set"; + 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"; - entity["temp_lo_stat_tpl"] = "{{ value_json }}"; + entity["temp_lo_stat_t"] = topic_root + TEMP_LO_STATE; modes.add("heat"); modes.add("auto"); } @@ -91,12 +153,39 @@ void climateDevice(String name, boolean multimode=false) { ids.add(chipid); //dev["ids"] = "[\"" + chipid +"\"]"; + DEVICE_LIST[TOTAL_DEVICES] = device; + TOTAL_DEVICES++; hass_comm.mqtt_discovery(config_topic, entity); } +void SendUpdate(ControlDevice device) { + int setpoint_count = 0; + float device_temp; + device_temp = UpdateTemperature(&sensors, device.temp_sensor); + hass_comm.publish_data(device.current_temp_topic, String(device_temp)); + hass_comm.publish_data(device.mode.StateTopic, device.mode.Setting); + hass_comm.publish_data(device.action_topic, device.action_state); + bool dual_mode = device.dual; + + if (dual_mode) { + setpoint_count = 3; + } else { + setpoint_count = 1; + } + + for (int i=0;i= 10000) { + if (DeviceLoop(DEVICE_LIST[0], &sensors)) { + Serial.println("Ouput state changed."); + SendUpdate(DEVICE_LIST[0]); + } + if (DeviceLoop(DEVICE_LIST[1], &sensors)) { + Serial.println("Ouput state changed."); + SendUpdate(DEVICE_LIST[1]); + } + if (DeviceLoop(DEVICE_LIST[2], &sensors)) { + Serial.println("Ouput state changed."); + SendUpdate(DEVICE_LIST[2]); + } + sensors.requestTemperatures(); + SendUpdate(DEVICE_LIST[0]); + SendUpdate(DEVICE_LIST[1]); + SendUpdate(DEVICE_LIST[2]); + lastMillis = currentMillis; + } + + delay(100); hass_comm.loop(); } diff --git a/src/FermController/device.h b/src/FermController/device.h deleted file mode 100644 index 621d682..0000000 --- a/src/FermController/device.h +++ /dev/null @@ -1,79 +0,0 @@ -#include - -class Device { - public: - - String device_topic; - Communicator hass_comm; - - Device(Communicator& comm, String topic) { - this->hass_comm = comm; - this->device_topic = topic; - } - - - void addParameter(String id, String value){ - _params[_paramcount] = Parameter(id, value); - _paramcount++ - } - - void attachEntity(Entity entity){ - _entities[_entitycount] = entity; - _entitycount++; - } - - void registerDevice(){ - - for (int i=0;i<_entitycount;i++){ - StaticJsonDocument<512> doc; - for (int j=0;j<_entities[i].paramcount;j++) { - doc[_entities[i].params[j].getID()] = _entities[i].params[j].getValue(); - } - } - JsonObject dev = doc.createNestedObject("dev"); - for (int i=0; i<_paramcount;i++) { - dev[_params[j].getID()] = _params[j].getValue(); - } - - //Register - hass_comm.mqtt_discovery(device_topic, doc); - } - - private: - Parameter _params[]; - int _paramcount = 0; - Entity _entities[]; - int _entitycount = 0; - -} - -class Entity { - public: - Parameter params[]; - int paramcount = 0; - void addParameter(String id, String value){ - params[paramcount] = Parameter(id, value); - paramcount++ - } - -} - -class Parameter { - public: - Parameter(String id, String value){ - this->_id = id; - this->_value = value; - } - - String getID(){ - return this->_id; - } - - String getValue(){ - return this->_value; - } - - private: - String _id; - String _value; -} \ No newline at end of file