From 6276cfe455d8d1629d2d8d56d61840e6bb174214 Mon Sep 17 00:00:00 2001 From: Chris Giacofei Date: Mon, 13 Sep 2021 07:39:33 -0400 Subject: [PATCH] Initial Commit. --- boil_kettle/boil_kettle.ino | 184 ++++++++++++++++++++++++++++++++++++ boil_kettle/mqtt.ino | 70 ++++++++++++++ boil_kettle/slowPWM.ino | 28 ++++++ 3 files changed, 282 insertions(+) create mode 100644 boil_kettle/boil_kettle.ino create mode 100644 boil_kettle/mqtt.ino create mode 100644 boil_kettle/slowPWM.ino diff --git a/boil_kettle/boil_kettle.ino b/boil_kettle/boil_kettle.ino new file mode 100644 index 0000000..de18ded --- /dev/null +++ b/boil_kettle/boil_kettle.ino @@ -0,0 +1,184 @@ +/** Home Assitant Sensor + sensor: + - platform: mqtt + name: "boil_kettle_setpoint" + state_topic: "brewery/sensor/boil_kettle" + unit_of_measurement: {{ value_json.units }} + value_template: "{{ value_json.setpoint }}" +*/ + +/**Home Assistant Automation + - alias: Set BK Input Value + trigger: + platform: mqtt + topic: "brewery/sensor/boil_kettle" + action: + service: input_select.select_option + data: + entity_id: input_select.boil_kettle_pwm + option: "{{ trigger.payload.setpoint }}" + + # This automation script runs when the thermostat mode selector is changed. + # It publishes its value to the same MQTT topic it is also subscribed to. + - alias: Set BK PWM + trigger: + platform: state + entity_id: input_select.boil_kettle_pwm + action: + service: mqtt.publish + data: + topic: "brewery/setpoint/bk" + retain: true + payload: "setpoint: {{ states('input_select.thermostat_mode') }}" + +*/ + +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include + +// Pin definitions +#define encoderCLK 2 +#define encoderDT 3 +#define encoderBTN 4 +#define kettlePWM 5 + +#define TOPIC_PREFIX "brewery/" +#define BKTOPIC "setpoint/bk" + +// Global variables. +byte kettle_duty = 0; +bool kettle_on = false; + +// User I/O objects. +Button enter(encoderBTN); +MD_REncoder rotary = MD_REncoder(encoderDT, encoderCLK); +LiquidCrystal_I2C lcd(0x27,20,4); + +float output = 0; +int updateInterval = 1000; + +byte mac[] = { 0xA6, 0x61, 0x0A, 0xAE, 0x89, 0xDE }; //physical mac address +IPAddress ip(192,168,1,177); +IPAddress myDns(192, 168, 0, 1); + +EthernetClient net; +MQTTClient mqtt_client; + +unsigned long lastRun = 0; + +// Return a character array to represent the +// On/Off state of the kettle. +char* kettle_state() { + if (kettle_on) { + return (char*)"On"; + } else { + return (char*)"Off"; + } +} + +// Interrupt function to run when encoder is turned. +// +// Increases/decreases the kettle output to a max +// of 100% and minimum of 0%. +void doEncoder() +{ + uint8_t result = rotary.read(); + + if (result == DIR_CW && kettle_duty < 100) { + kettle_duty++; + } else if (result == DIR_CCW && kettle_duty > 0) { + kettle_duty--; + } + +} + +// LCD menu setup. + +LiquidLine kettle_state_line(0, 0, "Boil Kettle ", kettle_state); +LiquidLine kettle_power_line(0, 1, "Kettle Power % ", kettle_duty); + +LiquidScreen home_screen(kettle_state_line, kettle_power_line); + +LiquidMenu menu(lcd); + +void setup() { + + unsigned long lastRun = millis() - updateInterval; + Serial.begin(9600); + rotary.begin(); + Ethernet.begin(mac, ip); + Serial.println("Setting up..."); + + attachInterrupt(digitalPinToInterrupt(encoderCLK), doEncoder, CHANGE); + attachInterrupt(digitalPinToInterrupt(encoderDT), doEncoder, CHANGE); + + pinMode(encoderCLK, INPUT_PULLUP); + pinMode(encoderDT, INPUT_PULLUP); + pinMode(encoderBTN, INPUT_PULLUP); + + // if you get a connection, report back via serial: + if (net.connect("www.google.com", 80)) { + Serial.print("you connected to "); + Serial.println(net.remoteIP()); + // Make a HTTP request: + net.println("GET /search?q=arduino HTTP/1.1"); + net.println("Host: www.google.com"); + net.println("Connection: close"); + net.println(); + SetupMQTT("192.168.1.198"); + } else { + // if you didn't get a connection to the server: + Serial.println("connection failed"); + } + + lcd.init(); + lcd.backlight(); + + menu.init(); + menu.add_screen(home_screen); + menu.update(); + +}; + +void update_boil_kettle(){ + static byte last_kettle_duty = 0; + + if (enter.pressed()) { + kettle_on = !kettle_on; + menu.update(); + } + + if (last_kettle_duty != kettle_duty) { + last_kettle_duty = kettle_duty; + menu.update(); + } + + if (kettle_on) { + slowPWM(kettlePWM, kettle_duty, 1000); + } else { + slowPWM(kettlePWM, 0, 1000); + } +} + +void loop() { + update_boil_kettle(); + + unsigned long elapsedTime = (millis() - lastRun); + + if (elapsedTime >= updateInterval) { + mqtt_client.loop(); + //if (!mqtt_client.connected()) ConnectMQTT(); + + sendSensorData(); + lastRun = millis(); + } + +} diff --git a/boil_kettle/mqtt.ino b/boil_kettle/mqtt.ino new file mode 100644 index 0000000..26f0cf7 --- /dev/null +++ b/boil_kettle/mqtt.ino @@ -0,0 +1,70 @@ +void ConnectMQTT() { + Serial.println("connecting MQTT..."); + while (!mqtt_client.connect("arduino", "mqtt_user", "4SutKhR2ZEET2IU0PNhH")) { + Serial.print("."); + delay(1000); + } + + Serial.println("\nconnected!"); + mqtt_client.subscribe("brewery/setpoint/bk"); +} + +void MessageReceived(String &topic, String &payload) { + Serial.println("incoming: " + topic + " - " + payload); + + /** JSON Parser Setup */ + StaticJsonDocument<200> doc; + + // Deserialize the JSON document + DeserializationError error = deserializeJson(doc, payload); + + // Test if parsing succeeds. + if (error) { + Serial.print(F("deserializeJson() failed: ")); + Serial.println(error.f_str()); + return; + } + char buf[30]; + strcpy(buf,TOPIC_PREFIX); + strcat(buf,BKTOPIC); + if (topic == buf) { + // Update PWM setpoint. + String name = doc["entity"]; + String setting = doc["setpoint"]; + + kettle_duty = setting.toInt(); + String unit = doc["units"]; + + Serial.println("Updating setpoint for " + name + " to " + setting + " " + unit); + } +} + +void SetupMQTT(String broker) { + // Note: Local domain names (e.g. "Computer.local" on OSX) are not supported + // by Arduino. You need to set the IP address directly. + Serial.println("Setup MQTT client."); + mqtt_client.begin("192.168.1.198", net); + mqtt_client.onMessage(MessageReceived); + + ConnectMQTT(); +} + +static void sendSensorData() { + Serial.println("Sending data..."); + + // NOTE: max message length is 250 bytes. + StaticJsonDocument<200> doc; + + doc["entity"] = "boil_kettle"; + doc["setpoint"] = kettle_duty; + doc["units"] = "%"; + + String jstr; + serializeJson(doc, jstr); + + String topic = TOPIC_PREFIX; + topic += "sensor/boil_kettle"; + + mqtt_client.publish(topic, jstr); + +} diff --git a/boil_kettle/slowPWM.ino b/boil_kettle/slowPWM.ino new file mode 100644 index 0000000..5ec4777 --- /dev/null +++ b/boil_kettle/slowPWM.ino @@ -0,0 +1,28 @@ +// Bit bang low frequency PWM. +// +// Parameters: +// outputPin (byte) - Pin number to output PWM. +// dutyCycle (byte) - PWM period from 0 - 100. +void slowPWM(byte outputPin, byte dutyCycle, unsigned long period) +{ + pinMode(outputPin, OUTPUT); + static byte outputState = LOW; + static unsigned long lastSwitchTime = 0; + + unsigned long onTime = (dutyCycle * period) / 100; + unsigned long offTime = period - onTime; + + unsigned long currentTime = millis(); + + if (outputState == HIGH && (currentTime - lastSwitchTime >= onTime)) + { + lastSwitchTime = currentTime; + outputState = LOW; + } + if (outputState == LOW && (currentTime - lastSwitchTime >= offTime)) + { + lastSwitchTime = currentTime; + outputState = HIGH; + } + digitalWrite(outputPin, outputState); +}