First version. Saves target t to flash, features cut-off by relay temperature.
This commit is contained in:
@@ -0,0 +1 @@
|
||||
config.h
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
#define WIFI_NAME "your wi-fi name"
|
||||
#define WIFI_PASS "you wi-fi password"
|
||||
#define MY_IP 192,168,0,26
|
||||
#define MY_GW 192,168,0,1
|
||||
#define MY_NET 255,255,255,0
|
||||
#define SENSOR_IP 192,168,0,25
|
||||
#define SENSOR_PORT 15000
|
||||
+331
@@ -0,0 +1,331 @@
|
||||
#include "DHT.h"
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <ModbusIP_ESP8266.h>
|
||||
#include "config.h"
|
||||
#include "LittleFS.h"
|
||||
|
||||
// network
|
||||
|
||||
IPAddress my_ip(MY_IP);
|
||||
IPAddress gateway(MY_GW);
|
||||
IPAddress subnet(MY_NET);
|
||||
IPAddress sensor_ip(SENSOR_IP);
|
||||
|
||||
// After how much time without connection we will
|
||||
// reboot (in millis)
|
||||
#define NON_CONNECTED_REBOOT_TIMEOUT 60000
|
||||
|
||||
// dht sensor
|
||||
#define DHTPIN D6
|
||||
#define DHTTYPE AM2301
|
||||
DHT dht(DHTPIN, DHTTYPE);
|
||||
#define DHT_UPDATE_INTERVAL 500
|
||||
|
||||
// relay
|
||||
#define RELAY_PIN D1
|
||||
#define T_UPDATE_INTERVAL 500
|
||||
|
||||
// external t
|
||||
#define EXTERNAL_UPDATE_INTERVAL 1000
|
||||
|
||||
// logic consts
|
||||
|
||||
// deviation from target t allowed (in degrees multiplied by 100)
|
||||
#define T_HYSTERESIS 150
|
||||
// timeout to switch from external to internal temperature data (in milliseconds)
|
||||
#define SWITCH_TO_INTERNAL_TIMEOUT 50000
|
||||
|
||||
// modbus consts and works
|
||||
#define TCP_PORT 15000
|
||||
|
||||
#define MODE_AUTO 0
|
||||
#define MODE_MANUAL 1
|
||||
|
||||
#define REGISTER_MODE 0
|
||||
#define REGISTER_CUR_STATE 1
|
||||
#define REGISTER_RELAY_TEMP 2
|
||||
#define REGISTER_OVERHEAT 3
|
||||
#define REGISTER_OVERRIDE_STATE 4
|
||||
// desired temperature
|
||||
#define REGISTER_TARGET_T 5
|
||||
// temperature from internal sensor
|
||||
#define REGISTER_INTERNAL_T 6
|
||||
// temperatur from external sensor (received via modbus-tcp)
|
||||
#define REGISTER_EXTERNAL_T 7
|
||||
|
||||
// critical relay temp (degrees multiplied by 100)
|
||||
#define CRITICAL_T 6000
|
||||
|
||||
ModbusIP mb;
|
||||
|
||||
char id[64];
|
||||
|
||||
|
||||
int cur_mode = MODE_AUTO;
|
||||
int cur_state = 0;
|
||||
int relay_t = 0;
|
||||
int overheat = 0;
|
||||
int override_state = 0;
|
||||
int target_t = 0;
|
||||
int internal_t = 0;
|
||||
int external_t = 0;
|
||||
|
||||
void LoadState()
|
||||
{
|
||||
File f = LittleFS.open("/target_t.txt", "r");
|
||||
if (!f) return;
|
||||
String s = f.readStringUntil('\n');
|
||||
target_t = atoi(s.c_str());
|
||||
f.close();
|
||||
}
|
||||
|
||||
void SaveState()
|
||||
{
|
||||
File f = LittleFS.open("/target_t.txt", "w");
|
||||
if (!f) return;
|
||||
f.println(target_t);
|
||||
f.close();
|
||||
}
|
||||
|
||||
void setup()
|
||||
{
|
||||
if (!LittleFS.begin())
|
||||
{
|
||||
Serial.println("Unable to mount the filesystem");
|
||||
ESP.restart();
|
||||
}
|
||||
LoadState();
|
||||
pinMode(DHTPIN, INPUT);
|
||||
pinMode(RELAY_PIN, OUTPUT);
|
||||
dht.begin();
|
||||
Serial.begin(115200);
|
||||
Serial.println("Initialised...");
|
||||
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.config(my_ip, gateway, subnet);
|
||||
WiFi.setAutoReconnect(true);
|
||||
WiFi.begin(WIFI_NAME, WIFI_PASS);
|
||||
Serial.print("Connecting");
|
||||
unsigned long start = millis();
|
||||
while (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
delay(500);
|
||||
Serial.print(".");
|
||||
if (millis() - start > NON_CONNECTED_REBOOT_TIMEOUT)
|
||||
{
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
Serial.println();
|
||||
|
||||
Serial.print("Connected, IP address: ");
|
||||
Serial.println(WiFi.localIP());
|
||||
uint8_t macAddr[6];
|
||||
WiFi.macAddress(macAddr);
|
||||
sprintf(id, "%02x:%02x:%02x:%02x:%02x:%02x", macAddr[0], macAddr[1], macAddr[2], macAddr[3], macAddr[4], macAddr[5]);
|
||||
Serial.printf("Connected, mac address: %s\n", id);
|
||||
|
||||
mb.server(15000);
|
||||
mb.addHreg(REGISTER_MODE, cur_mode);
|
||||
mb.addHreg(REGISTER_CUR_STATE, cur_state);
|
||||
mb.addHreg(REGISTER_RELAY_TEMP, relay_t);
|
||||
mb.addHreg(REGISTER_OVERHEAT, overheat);
|
||||
mb.addHreg(REGISTER_OVERRIDE_STATE, override_state);
|
||||
mb.addHreg(REGISTER_TARGET_T, target_t);
|
||||
mb.addHreg(REGISTER_INTERNAL_T, internal_t);
|
||||
mb.addHreg(REGISTER_EXTERNAL_T, external_t);
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////
|
||||
// thermistor (relay temperature)
|
||||
//////////////////////////////////////////////////////////////////
|
||||
#define THERMISTORPIN A0
|
||||
#define THERMISTORNOMINAL 10000
|
||||
#define TEMPERATURENOMINAL 25
|
||||
#define NUMSAMPLES 5
|
||||
#define BCOEFFICIENT 3950
|
||||
#define SERIESRESISTOR 10000
|
||||
|
||||
unsigned long last_relay_update = 0;
|
||||
|
||||
void UpdateRelayT()
|
||||
{
|
||||
float average = 0;
|
||||
for (int i = 0; i < NUMSAMPLES; i++)
|
||||
{
|
||||
average += analogRead(THERMISTORPIN);
|
||||
delay(10);
|
||||
}
|
||||
average /= NUMSAMPLES;
|
||||
average = 1023 / average - 1;
|
||||
average = SERIESRESISTOR / average;
|
||||
float steinhart;
|
||||
steinhart = average / THERMISTORNOMINAL; // (R/Ro)
|
||||
steinhart = log(steinhart); // ln(R/Ro)
|
||||
steinhart /= BCOEFFICIENT; // 1/B * ln(R/Ro)
|
||||
steinhart += 1.0 / (TEMPERATURENOMINAL + 273.15); // + (1/To)
|
||||
steinhart = 1.0 / steinhart; // инвертируем
|
||||
steinhart -= 273.15; // конвертируем в градусы по Цельсию
|
||||
relay_t = int(steinhart * 100);
|
||||
last_relay_update = millis();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// DHT (internal thermo sensor)
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
unsigned long last_dht_update = 0;
|
||||
|
||||
void UpdateDHT()
|
||||
{
|
||||
float t = dht.readTemperature();
|
||||
internal_t = (int)(t * 100);
|
||||
last_dht_update = millis();
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
// Modbus client (external sensor t)
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#define ANSWER_READ_TIMEOUT 1000
|
||||
|
||||
unsigned long last_external_update = 0;
|
||||
bool was_external_update = false;
|
||||
|
||||
WiFiClient client;
|
||||
|
||||
#define SENSOR_T_REGISTER 0
|
||||
uint8_t mb_message[50];
|
||||
uint8_t mb_answer[50];
|
||||
int mb_answer_len;
|
||||
|
||||
void UpdateExternalT()
|
||||
{
|
||||
if (!client.connect(sensor_ip, SENSOR_PORT))
|
||||
{
|
||||
Serial.println("Unable to connect to sensor");
|
||||
return;
|
||||
}
|
||||
mb_message[0] = 0; // transaction id
|
||||
mb_message[1] = 0;
|
||||
mb_message[2] = 0; // protocol id
|
||||
mb_message[3] = 0;
|
||||
mb_message[4] = 0; // length
|
||||
mb_message[5] = 6;
|
||||
mb_message[6] = 0; // unit id
|
||||
mb_message[7] = 3; // function
|
||||
mb_message[8] = (SENSOR_T_REGISTER >> 8) & 0xFF; // address - high
|
||||
mb_message[9] = (SENSOR_T_REGISTER) & 0xFF; // address - low
|
||||
mb_message[10] = 0; // amount
|
||||
mb_message[11] = 1;
|
||||
client.write(mb_message, 12);
|
||||
client.flush();
|
||||
// read answer (it should be 8 (header) + 1 (amount received bytes) + 2 (actual data)
|
||||
mb_answer_len = 0;
|
||||
unsigned long start = millis();
|
||||
while (millis() - start < ANSWER_READ_TIMEOUT && mb_answer_len < 11)
|
||||
{
|
||||
int result = client.read();
|
||||
if (result != -1)
|
||||
{
|
||||
mb_answer[mb_answer_len++] = result;
|
||||
}
|
||||
}
|
||||
if (mb_answer_len < 11)
|
||||
{
|
||||
Serial.println("Too short answer from sensor");
|
||||
return;
|
||||
}
|
||||
external_t = (((int)mb_answer[9]) << 8) | ((int)mb_answer[10]);
|
||||
last_external_update = millis();
|
||||
was_external_update = true;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////
|
||||
|
||||
int GetCurrentT()
|
||||
{
|
||||
unsigned long current = millis();
|
||||
// case when we did not receive external T since device boot
|
||||
if (!was_external_update) return internal_t;
|
||||
if (current < last_external_update || current - last_external_update < SWITCH_TO_INTERNAL_TIMEOUT)
|
||||
{
|
||||
return external_t;
|
||||
}
|
||||
return internal_t;
|
||||
}
|
||||
|
||||
unsigned long last_connected = 0;
|
||||
|
||||
void loop()
|
||||
{
|
||||
unsigned long current = millis();
|
||||
int u = 0;
|
||||
if (current < last_relay_update || current - last_relay_update >= T_UPDATE_INTERVAL)
|
||||
{
|
||||
UpdateRelayT();
|
||||
u = 1;
|
||||
}
|
||||
if (current < last_dht_update || current - last_dht_update >= DHT_UPDATE_INTERVAL)
|
||||
{
|
||||
UpdateDHT();
|
||||
u = 1;
|
||||
}
|
||||
if (current < last_external_update || current - last_external_update >= EXTERNAL_UPDATE_INTERVAL)
|
||||
{
|
||||
UpdateExternalT();
|
||||
u = 1;
|
||||
}
|
||||
mb.task();
|
||||
cur_mode = mb.Hreg(REGISTER_MODE);
|
||||
override_state = mb.Hreg(REGISTER_OVERRIDE_STATE);
|
||||
target_t = mb.Hreg(REGISTER_TARGET_T);
|
||||
int current_t = GetCurrentT();
|
||||
if (cur_mode == MODE_AUTO)
|
||||
{
|
||||
if (current_t < target_t - T_HYSTERESIS)
|
||||
{
|
||||
cur_state = 1;
|
||||
}
|
||||
else if (current_t > target_t + T_HYSTERESIS)
|
||||
{
|
||||
cur_state = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cur_state = override_state;
|
||||
}
|
||||
if (relay_t > CRITICAL_T)
|
||||
{
|
||||
Serial.printf("Critical overheat, t = %d\n", relay_t);
|
||||
cur_state = 0;
|
||||
overheat = 1;
|
||||
}
|
||||
else overheat = 0;
|
||||
digitalWrite(RELAY_PIN, cur_state);
|
||||
if (u)
|
||||
{
|
||||
Serial.printf("State %d Relay t %d Internal t %d External t %d Current t %d Target t %d\n", cur_state, relay_t, internal_t, external_t, current_t, target_t);
|
||||
SaveState();
|
||||
}
|
||||
mb.Hreg(REGISTER_MODE, cur_mode);
|
||||
mb.Hreg(REGISTER_RELAY_TEMP, relay_t);
|
||||
mb.Hreg(REGISTER_OVERHEAT, overheat);
|
||||
mb.Hreg(REGISTER_CUR_STATE, cur_state);
|
||||
mb.Hreg(REGISTER_INTERNAL_T, internal_t);
|
||||
mb.Hreg(REGISTER_EXTERNAL_T, external_t);
|
||||
if (WiFi.status() != WL_CONNECTED)
|
||||
{
|
||||
WiFi.reconnect();
|
||||
}
|
||||
else
|
||||
{
|
||||
last_connected = current;
|
||||
}
|
||||
if (current < last_connected || current - last_connected > NON_CONNECTED_REBOOT_TIMEOUT)
|
||||
{
|
||||
SaveState();
|
||||
ESP.restart();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
Number HEATER_NAME_Mode "HEATER_NAME mode" <switch> {channel="modbus:data:HEATER_NAME:H:HEATER_NAME_Mode:number"}
|
||||
Switch HEATER_NAME_State "HEATER_NAME state" <switch> {channel="modbus:data:HEATER_NAME:H:HEATER_NAME_State:switch"}
|
||||
Number HEATER_NAME_RelayT "HEATER_NAME relay temperature [%.1f °C]" <temperature> {channel="modbus:data:HEATER_NAME:H:HEATER_NAME_RelayT:number"}
|
||||
Number HEATER_NAME_Overheat "HEATER_NAME relay overheated" <switch> {channel="modbus:data:HEATER_NAME:H:HEATER_NAME_Overheat:number"}
|
||||
Switch HEATER_NAME_OverrideState "HEATER_NAME override state" <switch> {channel="modbus:data:HEATER_NAME:H:HEATER_NAME_OverrideState:switch"}
|
||||
Number HEATER_NAME_TargetT "HEATER_NAME desired temperature [%.1f °C]" <temperature> {channel="modbus:data:HEATER_NAME:H:HEATER_NAME_TargetT:number"}
|
||||
Number HEATER_NAME_InternalT "HEATER_NAME internal temperature [%.1f °C]" <temperature> {channel="modbus:data:HEATER_NAME:H:HEATER_NAME_InternalT:number"}
|
||||
Number HEATER_NAME_ExternalT "HEATER_NAME external temperature [%.1f °C]" <temperature> {channel="modbus:data:HEATER_NAME:H:HEATER_NAME_ExternalT:number"}
|
||||
@@ -0,0 +1,12 @@
|
||||
Bridge modbus:tcp:HEATER_NAME [ host="192.168.0.24", port=15000, id=1 ] {
|
||||
Bridge poller H[start = 0, length = 8, type = "holding", refresh = 500] {
|
||||
Thing data HEATER_NAME_Mode[readStart = "0", readValueType = "int16", writeStart="0", writeValueType="int16", writeType="holding"]
|
||||
Thing data HEATER_NAME_State[readStart = "1", readValueType = "int16"]
|
||||
Thing data HEATER_NAME_RelayT[readStart = "2", readValueType = "int16", readTransform="JS(divide100.js)"]
|
||||
Thing data HEATER_NAME_Overheat[readStart = "3", readValueType = "int16"]
|
||||
Thing data HEATER_NAME_OverrideState[readStart = "4", readValueType = "int16", writeStart="4", writeValueType="int16", writeType="holding"]
|
||||
Thing data HEATER_NAME_TargetT[readStart = "5", readValueType = "int16", readTransform="JS(divide100.js)", writeStart="5", writeValueType="int16", writeType="holding", writeTransform="JS(multiply100.js)"]
|
||||
Thing data HEATER_NAME_InternalT[readStart = "6", readValueType = "int16", readTransform="JS(divide100.js)"]
|
||||
Thing data HEATER_NAME_ExternalT[readStart = "7", readValueType = "int16", readTransform="JS(divide100.js)"]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/perl
|
||||
|
||||
if (scalar(@ARGV) < 1)
|
||||
{
|
||||
die("Usage: make_config.pl HEATER_NAME");
|
||||
}
|
||||
my $name = shift @ARGV;
|
||||
|
||||
Process("heater_v3.things", $name.".things");
|
||||
Process("heater_v3.items", $name.".items");
|
||||
|
||||
sub Process
|
||||
{
|
||||
my $src = shift;
|
||||
my $dst = shift;
|
||||
my $fh;
|
||||
open($fh, "< ".$src);
|
||||
my $ofh;
|
||||
open($ofh, "> ".$dst);
|
||||
while (<$fh>)
|
||||
{
|
||||
$_ =~ s/HEATER_NAME/$name/g;
|
||||
print($ofh $_);
|
||||
}
|
||||
close($fh);
|
||||
close($ofh);
|
||||
}
|
||||
BIN
Binary file not shown.
Reference in New Issue
Block a user