Merging in current state of code. #3
3
.gitignore
vendored
3
.gitignore
vendored
@ -1,5 +1,5 @@
|
|||||||
.DS_Store
|
.DS_Store
|
||||||
bin/*/
|
bin/*
|
||||||
*build-*
|
*build-*
|
||||||
*build*
|
*build*
|
||||||
*~
|
*~
|
||||||
@ -9,4 +9,5 @@ doc/
|
|||||||
debug.txt
|
debug.txt
|
||||||
*.geany
|
*.geany
|
||||||
*.tags
|
*.tags
|
||||||
|
tags
|
||||||
*.bin
|
*.bin
|
||||||
|
@ -17,6 +17,8 @@ TARGET_DIR := $(BASE_DIR)/$(subst :,.,build/$(FQBN))
|
|||||||
BIN_DIR := $(BASE_DIR)/bin
|
BIN_DIR := $(BASE_DIR)/bin
|
||||||
SRC := $(wildcard $(SKETCH_DIR)/*.ino)
|
SRC := $(wildcard $(SKETCH_DIR)/*.ino)
|
||||||
HDRS := $(wildcard $(SKETCH_DIR)/*.h)
|
HDRS := $(wildcard $(SKETCH_DIR)/*.h)
|
||||||
|
LIBH := $(wildcard $(BASE_DIR)/lib/**/*.h)
|
||||||
|
LIBS := $(LIBH) $(wildcard $(BASE_DIR)/lib/**/*.cpp)
|
||||||
BIN := $(notdir $(SRC)).bin
|
BIN := $(notdir $(SRC)).bin
|
||||||
|
|
||||||
$(info FQBN is [${FQBN}])
|
$(info FQBN is [${FQBN}])
|
||||||
@ -36,7 +38,7 @@ all: $(BIN_DIR)/$(BIN) upload
|
|||||||
compile: $(BIN_DIR)/$(BIN)
|
compile: $(BIN_DIR)/$(BIN)
|
||||||
|
|
||||||
upload: $(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)
|
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
|
$(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
|
.PHONY: all compile test library upload ota clean monitor
|
||||||
|
|
||||||
$(TARGET_DIR)/$(BIN): $(SRC) $(HDRS)
|
$(TARGET_DIR)/$(BIN): $(SRC) $(HDRS) $(LIBS)
|
||||||
@ echo $(FQBN)
|
@ echo $(FQBN)
|
||||||
@ mkdir -p $(TARGET_DIR)
|
@ mkdir -p $(TARGET_DIR)
|
||||||
|
|
||||||
@ -73,4 +75,4 @@ $(TARGET_DIR)/$(BIN): $(SRC) $(HDRS)
|
|||||||
|
|
||||||
$(BIN_DIR)/$(BIN): $(TARGET_DIR)/$(BIN)
|
$(BIN_DIR)/$(BIN): $(TARGET_DIR)/$(BIN)
|
||||||
@ echo "$(BIN) $(BIN_DIR)/"
|
@ echo "$(BIN) $(BIN_DIR)/"
|
||||||
@ mv $(TARGET_DIR)/$(BIN) $(BIN_DIR)/
|
@ cp $(TARGET_DIR)/$(BIN) $(BIN_DIR)/
|
||||||
|
Binary file not shown.
@ -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->_net = network;
|
||||||
this->mqttCallback = cmd;
|
this->mqttCallback = mqttCallback;
|
||||||
this->_mqtt_client.setBufferSize(1536);
|
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() {
|
void Communicator::loop() {
|
||||||
_mqtt_client.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) {
|
void Communicator::mqtt_discovery(const String topic, StaticJsonDocument<1536> &entity) {
|
||||||
String payload;
|
String payload;
|
||||||
serializeJson(entity, payload);
|
serializeJson(entity, payload);
|
||||||
@ -89,21 +103,31 @@ void Communicator::mqtt_discovery(const String topic, StaticJsonDocument<1536> &
|
|||||||
{
|
{
|
||||||
Serial.print("Sending discovery payload to ");
|
Serial.print("Sending discovery payload to ");
|
||||||
Serial.println(topic);
|
Serial.println(topic);
|
||||||
Serial.println("");
|
|
||||||
Serial.println(payload);
|
Serial.println(payload);
|
||||||
Serial.println("");
|
|
||||||
|
|
||||||
_mqtt_client.publish(topic.c_str(), payload.c_str(), false);
|
_mqtt_client.publish(topic.c_str(), payload.c_str(), false);
|
||||||
|
|
||||||
if (entity.containsKey("mode_cmd_t")) _mqtt_client.subscribe(entity["mode_cmd_t"]);
|
const char* mode = entity["mode_cmd_t"];
|
||||||
if (entity.containsKey("temp_cmd_t")) _mqtt_client.subscribe(entity["temp_cmd_t"]);
|
if (mode) {
|
||||||
if (entity.containsKey("temp_hi_cmd_t")) _mqtt_client.subscribe(entity["temp_hi_cmd_t"]);
|
Subscribe(mode);
|
||||||
if (entity.containsKey("temp_lo_cmd_t")) _mqtt_client.subscribe(entity["temp_lo_cmd_t"]);
|
}
|
||||||
|
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();
|
_mqtt_client.loop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Communicator::publish_data(String topic, String value) {
|
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);
|
||||||
}
|
}
|
31
lib/Communicator/Communicator.h
Normal file
31
lib/Communicator/Communicator.h
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
#ifndef MyClass_h
|
||||||
|
#define MyClass_h
|
||||||
|
|
||||||
|
#include <PubSubClient.h>
|
||||||
|
#include <ArduinoJson.h>
|
||||||
|
#include <ESP8266WiFi.h>
|
||||||
|
|
||||||
|
#include <Global.h>
|
||||||
|
#include <secrets.h>
|
||||||
|
|
||||||
|
#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
|
@ -1,25 +0,0 @@
|
|||||||
#ifndef MyClass_h
|
|
||||||
#define MyClass_h
|
|
||||||
|
|
||||||
#include <PubSubClient.h>
|
|
||||||
#include <ArduinoJson.h>
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
|
|
||||||
#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
|
|
@ -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<DOC_SIZE> 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;
|
|
||||||
|
|
||||||
}
|
|
@ -4,7 +4,7 @@
|
|||||||
#include <PubSubClient.h>
|
#include <PubSubClient.h>
|
||||||
#include <OneWire.h>
|
#include <OneWire.h>
|
||||||
#include <DallasTemperature.h>
|
#include <DallasTemperature.h>
|
||||||
#include <ESP8266WiFi.h>
|
#include <ESP8266WiFi.h>
|
||||||
|
|
||||||
#include <Tools.h>
|
#include <Tools.h>
|
||||||
#include <secrets.h>
|
#include <secrets.h>
|
||||||
@ -14,6 +14,8 @@ const char version[] = "0.0.1";
|
|||||||
#define ACTION_TPC "action/state"
|
#define ACTION_TPC "action/state"
|
||||||
#define MODE_SET "mode/set"
|
#define MODE_SET "mode/set"
|
||||||
#define MODE_STATE "mode/state"
|
#define MODE_STATE "mode/state"
|
||||||
|
#define SWING_STATE "swing/state"
|
||||||
|
#define SWING_SET "swing/set"
|
||||||
#define TEMP_SET "temp/set"
|
#define TEMP_SET "temp/set"
|
||||||
#define TEMP_STATE "temp/state"
|
#define TEMP_STATE "temp/state"
|
||||||
#define TEMP_CURRENT "temp/current"
|
#define TEMP_CURRENT "temp/current"
|
||||||
@ -29,17 +31,17 @@ const char version[] = "0.0.1";
|
|||||||
|
|
||||||
#ifndef DEVICE_NAME
|
#ifndef DEVICE_NAME
|
||||||
#define DEVICE_NAME "MQTT Thermostat"
|
#define DEVICE_NAME "MQTT Thermostat"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DEVICE_MDL
|
#ifndef DEVICE_MDL
|
||||||
#define DEVICE_MDL "Thermostat"
|
#define DEVICE_MDL "Thermostat"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef DEVICE_MF
|
#ifndef DEVICE_MF
|
||||||
#define DEVICE_MF ""
|
#define DEVICE_MF ""
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifndef ROOT
|
#ifndef ROOT
|
||||||
#define ROOT "thermostat/"
|
#define ROOT "thermostat/"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -53,29 +55,129 @@ const char version[] = "0.0.1";
|
|||||||
|
|
||||||
#define TOPIC_LIMIT 4
|
#define TOPIC_LIMIT 4
|
||||||
|
|
||||||
//COMMAND_SIGNATURE void (*commandFunction)(uint8_t*) // https://forum.arduino.cc/t/assignment-of-function/528949/3
|
//~ #define OFF 0
|
||||||
//CALLBACK_SIGNATURE void (*callback)(char*, uint8_t*, unsigned int)
|
//~ #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 {
|
struct CommandTopic {
|
||||||
void (*cmd)(byte*);
|
//~ ptr CmdFunc;
|
||||||
|
byte* Setting;
|
||||||
String CmdTopic;
|
String CmdTopic;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Mode {
|
||||||
|
String CmdTopic;
|
||||||
|
String StateTopic;
|
||||||
|
String Setting;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SetPoint {
|
||||||
|
String CmdTopic;
|
||||||
|
String StateTopic;
|
||||||
|
int Setting;
|
||||||
|
};
|
||||||
|
|
||||||
struct ControlDevice {
|
struct ControlDevice {
|
||||||
String name; // "Glycol Chiller"
|
String name; // "Glycol Chiller"
|
||||||
String topic_root; // "brewhouse/"
|
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) {
|
float UpdateTemperature(DallasTemperature *sensors, DeviceAddress glycol_tank) {
|
||||||
for (int i=0;i<device_count;i++) {
|
// method 2 - faster
|
||||||
ControlDevice this_device = devices[i];
|
float tempC = sensors->getTempC(glycol_tank);
|
||||||
for (int j=0;j<TOPIC_LIMIT;j++) {
|
if(tempC == DEVICE_DISCONNECTED_C)
|
||||||
if (this_device.command_topics[j]->CmdTopic == command_topic) return this_device.command_topics[j]->cmd;
|
{
|
||||||
}
|
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
|
#endif
|
||||||
|
@ -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";
|
|
@ -7,11 +7,17 @@ source repository.
|
|||||||
#define GLOBAL_H
|
#define GLOBAL_H
|
||||||
|
|
||||||
#define DEVICE_SW "0.0.1"
|
#define DEVICE_SW "0.0.1"
|
||||||
auto chipid = String(ESP.getChipId(), HEX);
|
|
||||||
|
|
||||||
#define DEVICE_NAME "Glycol Chiller"
|
#define DEVICE_NAME "Glycol Chiller"
|
||||||
#define DEVICE_MDL "Chillenator v0.1"
|
#define DEVICE_MDL "Chillenator v0.1"
|
||||||
#define DEVICE_MF "Damn Yankee Brewing"
|
#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
|
#endif
|
||||||
|
@ -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);
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
#ifndef MyClass_h
|
|
||||||
#define MyClass_h
|
|
||||||
|
|
||||||
#include <PubSubClient.h>
|
|
||||||
#include <ArduinoJson.h>
|
|
||||||
#include <ESP8266WiFi.h>
|
|
||||||
|
|
||||||
#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
|
|
@ -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
|
|
@ -1,42 +0,0 @@
|
|||||||
#ifndef SLOWPWM_h
|
|
||||||
#define SLOWPWM_h
|
|
||||||
|
|
||||||
#include <Arduino.h>
|
|
||||||
|
|
||||||
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
|
|
@ -1,33 +1,68 @@
|
|||||||
//#include <PubSubClient.h>
|
//#include <PubSubClient.h>
|
||||||
#include <ArduinoJson.h>
|
#include <ArduinoJson.h>
|
||||||
#include <IPAddress.h>
|
#include <IPAddress.h>
|
||||||
|
#include <DallasTemperature.h>
|
||||||
|
|
||||||
// My Libraries
|
// My Libraries
|
||||||
#include <secrets.h>
|
#include <secrets.h>
|
||||||
#include <global.h>
|
#include <Global.h>
|
||||||
#include <communicator.h>
|
#include <Communicator.h>
|
||||||
|
#include <Device.h>
|
||||||
String chiller_state = "idle";
|
|
||||||
int tank_setpoint = 28;
|
|
||||||
|
|
||||||
|
OneWire oneWire(ONE_WIRE_BUS);
|
||||||
|
DallasTemperature sensors(&oneWire);
|
||||||
WiFiClient net;
|
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) {
|
void mqttCallback(char *topic, byte *payload, unsigned int length) {
|
||||||
Serial.print("incoming: ");
|
payload[length] = '\0';
|
||||||
Serial.println(topic);
|
|
||||||
for (unsigned int i = 0; i < length; i++)
|
for (int i=0;i<TOTAL_DEVICES;i++) {
|
||||||
{
|
if (strcmp((char *)topic, (DEVICE_LIST[i].mode.CmdTopic).c_str() ) == 0){
|
||||||
Serial.print((char)payload[i]);
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Serial.println("");
|
|
||||||
}
|
|
||||||
|
|
||||||
Communicator hass_comm = Communicator(net, &mqttCallback);
|
|
||||||
|
|
||||||
String slugify(String input) {
|
|
||||||
input.toLowerCase();
|
|
||||||
input.replace(" ", "_");
|
|
||||||
return input;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void merge(JsonObject dest, JsonObjectConst src) {
|
void merge(JsonObject dest, JsonObjectConst src) {
|
||||||
@ -40,45 +75,72 @@ void merge(JsonObject dest, JsonObjectConst src) {
|
|||||||
*
|
*
|
||||||
* Using climate device.
|
* Using climate device.
|
||||||
*/
|
*/
|
||||||
void climateDevice(String name, boolean multimode=false) {
|
void climateDevice(String name, byte ds18b20[], bool multimode=false) {
|
||||||
auto chipid = String(ESP.getChipId(), HEX);
|
int setpoint_count = 0;
|
||||||
|
|
||||||
String name_slug = slugify(name);
|
String name_slug = slugify(name);
|
||||||
|
|
||||||
String config_topic = "homeassistant/climate/" + name_slug + "_" + chipid + "/config";
|
String config_topic = "homeassistant/climate/" + name_slug + "_" + chipid + "/config";
|
||||||
String topic_root = "brewhouse/" + name_slug + "/";
|
String topic_root = "brewhouse/" + name_slug + "/";
|
||||||
|
|
||||||
|
ControlDevice device;
|
||||||
|
device.dual = false;
|
||||||
|
device.name = name;
|
||||||
|
device.topic_root = "brewhouse/" + slugify(name) + "/";
|
||||||
|
|
||||||
|
for (int i=0;i<8;i++) {
|
||||||
|
device.temp_sensor[i] = ds18b20[i];
|
||||||
|
}
|
||||||
|
|
||||||
StaticJsonDocument<1536> entity;
|
StaticJsonDocument<1536> entity;
|
||||||
entity["uniq_id"] = chipid + "_" + name_slug;
|
entity["uniq_id"] = chipid + "_" + name_slug;
|
||||||
entity["name"] = name;
|
entity["name"] = name;
|
||||||
|
entity["value_template"] = "{{ value }}";
|
||||||
entity["temp_unit"] = "F";
|
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
|
// 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_cmd_tpl"] = "{{ value }}";
|
||||||
entity["mode_stat_t"] = topic_root + "mode/state";
|
entity["mode_stat_t"] = topic_root + MODE_STATE;
|
||||||
entity["mode_stat_tpl"] = "{{ value_json }}";
|
|
||||||
JsonArray modes = entity.createNestedArray("modes");
|
JsonArray modes = entity.createNestedArray("modes");
|
||||||
modes.add("off");
|
modes.add("off");
|
||||||
modes.add("cool");
|
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_cmd_tpl"] = "{{ value }}";
|
||||||
entity["temp_stat_t"] = topic_root + "temp/state";
|
entity["temp_stat_t"] = topic_root + TEMP_STATE;
|
||||||
entity["temp_stat_tpl"] = "{{ value_json }}";
|
entity["curr_temp_t"] = topic_root + TEMP_CURRENT;
|
||||||
entity["curr_temp_t"] = topic_root + "temp/current";
|
|
||||||
entity["curr_temp_tpl"] = "{{ value }}";
|
entity["curr_temp_tpl"] = "{{ value }}";
|
||||||
|
device.current_temp_topic= topic_root + TEMP_CURRENT;
|
||||||
|
|
||||||
if (multimode == true) {
|
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_cmd_tpl"] = "{{ value }}";
|
||||||
entity["temp_hi_stat_t"] = topic_root + "temp_hi/state";
|
entity["temp_hi_stat_t"] = topic_root + TEMP_HI_STATE;
|
||||||
entity["temp_hi_stat_tpl"] = "{{ value_json }}";
|
|
||||||
|
|
||||||
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_cmd_tpl"] = "{{ value }}";
|
||||||
entity["temp_lo_stat_t"] = topic_root + "temp_lo/state";
|
entity["temp_lo_stat_t"] = topic_root + TEMP_LO_STATE;
|
||||||
entity["temp_lo_stat_tpl"] = "{{ value_json }}";
|
|
||||||
modes.add("heat");
|
modes.add("heat");
|
||||||
modes.add("auto");
|
modes.add("auto");
|
||||||
}
|
}
|
||||||
@ -91,12 +153,39 @@ void climateDevice(String name, boolean multimode=false) {
|
|||||||
ids.add(chipid);
|
ids.add(chipid);
|
||||||
//dev["ids"] = "[\"" + chipid +"\"]";
|
//dev["ids"] = "[\"" + chipid +"\"]";
|
||||||
|
|
||||||
|
DEVICE_LIST[TOTAL_DEVICES] = device;
|
||||||
|
TOTAL_DEVICES++;
|
||||||
hass_comm.mqtt_discovery(config_topic, entity);
|
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<setpoint_count;i++) {
|
||||||
|
hass_comm.publish_data(device.setpoints[i].StateTopic, String(device.setpoints[i].Setting));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
|
hass_comm.connectCallback(&mqttCallback);
|
||||||
|
|
||||||
const char* ssid = WIFI_SSID;
|
const char* ssid = WIFI_SSID;
|
||||||
const char* password = WIFI_PASSWORD;
|
const char* password = WIFI_PASSWORD;
|
||||||
|
sensors.begin();
|
||||||
|
sensors.setResolution(glycol_ds18b20, 9);
|
||||||
|
|
||||||
Serial.begin(115200);
|
Serial.begin(115200);
|
||||||
WiFi.begin(ssid, password);
|
WiFi.begin(ssid, password);
|
||||||
|
|
||||||
@ -108,20 +197,41 @@ void setup() {
|
|||||||
Serial.print("IP Address: ");
|
Serial.print("IP Address: ");
|
||||||
Serial.println(WiFi.localIP());
|
Serial.println(WiFi.localIP());
|
||||||
|
|
||||||
climateDevice("Coolant Tank");
|
climateDevice("Coolant Tank", glycol_ds18b20);
|
||||||
|
climateDevice("Fermenter 1", fermA_ds18b20,true);
|
||||||
|
climateDevice("Fermenter 2", fermB_ds18b20,true);
|
||||||
|
|
||||||
String f_name = "Fermenter ";
|
SendUpdate(DEVICE_LIST[0]);
|
||||||
for (int i=0;i<FERMENTER_COUNT;i++) {
|
SendUpdate(DEVICE_LIST[1]);
|
||||||
int f_num = i+1;
|
SendUpdate(DEVICE_LIST[2]);
|
||||||
|
lastMillis = millis();
|
||||||
Serial.println(f_name + f_num);
|
|
||||||
climateDevice(f_name + f_num, true);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void loop() {
|
void loop() {
|
||||||
delay(5000);
|
currentMillis = millis();
|
||||||
hass_comm.publish_data("brewhouse/coolant_tank/temp/current", String(tank_setpoint));
|
|
||||||
hass_comm.publish_data("brewhouse/coolant_tank/mode/state", chiller_state);
|
|
||||||
|
|
||||||
|
if (currentMillis - lastMillis >= 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();
|
hass_comm.loop();
|
||||||
}
|
}
|
||||||
|
@ -1,79 +0,0 @@
|
|||||||
#include <ArduinoJson.h>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user