Initial Commit.
This commit is contained in:
commit
6276cfe455
184
boil_kettle/boil_kettle.ino
Normal file
184
boil_kettle/boil_kettle.ino
Normal file
@ -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 <ArduinoJson.h>
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <SPI.h>
|
||||||
|
#include <Ethernet.h>
|
||||||
|
#include <MQTT.h>
|
||||||
|
//#include <MQTTClient.h>
|
||||||
|
#include <LiquidCrystal_I2C.h>
|
||||||
|
#include <LiquidMenu.h>
|
||||||
|
#include <Button.h>
|
||||||
|
#include <MD_REncoder.h>
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
70
boil_kettle/mqtt.ino
Normal file
70
boil_kettle/mqtt.ino
Normal file
@ -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);
|
||||||
|
|
||||||
|
}
|
28
boil_kettle/slowPWM.ino
Normal file
28
boil_kettle/slowPWM.ino
Normal file
@ -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);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user