Well it compiles.

This commit is contained in:
Chris Giacofei 2023-01-25 21:41:56 -05:00
parent 0a7fa301ed
commit 4619ce1d17
8 changed files with 14 additions and 470 deletions

2
.gitignore vendored
View File

@ -1,5 +1,5 @@
.DS_Store
bin/*/
bin/*
*build-*
*build*
*~

View File

@ -73,4 +73,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)/

Binary file not shown.

View File

@ -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;
}

View File

@ -53,13 +53,13 @@ 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)
typedef void (*ptr)(uint8_t*);
//typedef ptr (*pm)();
const size_t DOC_SIZE = JSON_OBJECT_SIZE(29) + JSON_ARRAY_SIZE(4);
struct CommandTopic {
void (*cmd)(byte*);
ptr CmdFunc;
String CmdTopic;
};
@ -69,13 +69,18 @@ struct ControlDevice {
CommandTopic command_topics[TOPIC_LIMIT];
};
void (*cmd)(byte*) CmdLookup(String command_topic, ControlDevice devices[], int device_count) {
ptr CmdLookup(String lookup_topic, ControlDevice devices[], int device_count) {
for (int i=0;i<device_count;i++) {
ControlDevice this_device = devices[i];
for (int j=0;j<TOPIC_LIMIT;j++) {
if (this_device.command_topics[j]->CmdTopic == command_topic) return this_device.command_topics[j]->cmd;
CommandTopic this_topic = this_device.command_topics[j];
String topic = this_topic.CmdTopic;
if (topic == lookup_topic) {
return this_topic.CmdFunc;
}
}
}
return 0;
}
#endif

View File

@ -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

View File

@ -4,8 +4,9 @@
// My Libraries
#include <secrets.h>
#include <global.h>
#include <Global.h>
#include <communicator.h>
#include <Device.h>
String chiller_state = "idle";
int tank_setpoint = 28;
@ -24,12 +25,6 @@ void mqttCallback(char *topic, byte *payload, unsigned int length) {
Communicator hass_comm = Communicator(net, &mqttCallback);
String slugify(String input) {
input.toLowerCase();
input.replace(" ", "_");
return input;
}
void merge(JsonObject dest, JsonObjectConst src) {
for (auto kvp : src) {
dest[kvp.key()] = kvp.value();
@ -41,8 +36,6 @@ void merge(JsonObject dest, JsonObjectConst src) {
* Using climate device.
*/
void climateDevice(String name, boolean multimode=false) {
auto chipid = String(ESP.getChipId(), HEX);
String name_slug = slugify(name);
String config_topic = "homeassistant/climate/" + name_slug + "_" + chipid + "/config";

View File

@ -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;
}