Files
heater_v3/heater_v3.ino
T

342 lines
8.1 KiB
Arduino

#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
// relay cooldown temperature (after what t we allow to turn on relay again) (in degrees multiplied by 100)
#define RELAY_COOLDOWN_T 500
// 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 (!overheat)
{
if (relay_t > CRITICAL_T)
{
overheat = 1;
}
}
else
{
if (relay_t < CRITICAL_T - RELAY_COOLDOWN_T)
{
overheat = 0;
}
}
if (cur_state && !overheat) digitalWrite(RELAY_PIN, 1);
else digitalWrite(RELAY_PIN, 0);
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();
}
}