683 lines
23 KiB
C++
683 lines
23 KiB
C++
#include "HermitCrab.h"
|
|
#include "Config.h"
|
|
#include "AHT2x.h"
|
|
#include "NTC_10K.h"
|
|
#include "ZCD.h"
|
|
#include "History.h"
|
|
#include "TimeManager.h"
|
|
#include "ConnectWiFi.h"
|
|
#include "WiFiHost.h"
|
|
#include "OTA.h"
|
|
#include "UI.h"
|
|
#include "BLEScan.h"
|
|
|
|
#if defined(ESP32)
|
|
#include "esp_wifi.h"
|
|
#endif
|
|
#define TAG_MAIN "Main"
|
|
STATUS_TYPE status;
|
|
|
|
|
|
// Time
|
|
volatile unsigned short g_nYear, g_nMonth, g_nDay, g_nHour, g_nMinute, g_nSecond;
|
|
|
|
// Environment
|
|
bool bShowSensor = false;
|
|
time_t now;
|
|
struct tm timeinfo;
|
|
|
|
void readSensors();
|
|
void controlAC1(short temp, unsigned long tick);
|
|
void controlAC2(short temp, unsigned long tick);
|
|
void controlMist(short humid, unsigned long tick);
|
|
void controlFan(short temp, unsigned long tick);
|
|
void controlMotor(short hour, short min, unsigned long tick);
|
|
void controlLight(short hour, short min, unsigned long tick);
|
|
void controlFanDuty();
|
|
void controlMotorDuty();
|
|
void controlLightDuty();
|
|
|
|
|
|
// ==================================================================================
|
|
//
|
|
// Arduino Loop - controls
|
|
//
|
|
// ==================================================================================
|
|
MY_IRAM_ATTR void loop() {
|
|
static unsigned long lastTickSecond = 0;
|
|
static uint8_t lastSecond = -1;
|
|
unsigned long tickMillis = millis();
|
|
unsigned long tickSecond = tickMillis / 1000;
|
|
|
|
|
|
// Un-Conditional Loop
|
|
{
|
|
//ESP_LOGI(TAG_MAIN,"Checking WiFi2");
|
|
checkWiFi(tickMillis);
|
|
|
|
//ESP_LOGI(TAG_MAIN,"Host Loop");
|
|
host.Loop(tickMillis);
|
|
|
|
// UI Button Check
|
|
ui.loopButton(tickMillis);
|
|
}
|
|
|
|
// Every Second
|
|
if (tickSecond != lastTickSecond)
|
|
{
|
|
// Time and ZCD
|
|
setZCD();
|
|
setTime();
|
|
|
|
// Temperature and Humidity
|
|
readSensors();
|
|
//ble.loop(tickMillis);
|
|
|
|
// Fan, Motor, Light Duties
|
|
controlFanDuty();
|
|
controlMotorDuty();
|
|
controlLightDuty();
|
|
|
|
// Add to History - every minutes
|
|
if (g_nSecond == 0 && lastSecond != g_nSecond) {
|
|
history.add(status);
|
|
}
|
|
lastSecond = g_nSecond;
|
|
|
|
// Every 10 Second
|
|
switch(tickSecond % 10) {
|
|
case 1: // Every 5 second - xx:xx-x7
|
|
if (bShowSensor) {
|
|
ESP_LOGI(TAG_MAIN, "%s\n", printStatus(tickSecond, true));
|
|
}
|
|
break;
|
|
case 2: // AC1
|
|
controlAC1(status.nTemp1, tickSecond);
|
|
break;
|
|
case 3: // AC2
|
|
controlAC2(status.nTemp1, tickSecond);
|
|
break;
|
|
case 4: // Mist
|
|
controlMist(status.nHumid1, tickSecond);
|
|
break;
|
|
case 5: // Fan Control
|
|
controlFan(status.nTemp1, tickSecond);
|
|
break;
|
|
case 6: // Motor Control
|
|
controlMotor(g_nHour, g_nMinute, tickSecond);
|
|
break;
|
|
case 7: // Light Control
|
|
controlLight(g_nHour, g_nMinute, tickSecond);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
lastTickSecond = tickSecond;
|
|
}
|
|
yield();
|
|
}
|
|
// ==================================================================================
|
|
// End of Main Loop
|
|
// ==================================================================================
|
|
|
|
void readSensors() {
|
|
switch (config.nTemp1SensorType) {
|
|
case TEMP_SENSOR_TYPE::AHT20:
|
|
case TEMP_SENSOR_TYPE::AHT2x:
|
|
status.nTemp1 = (aht25.getTemperature() + 5) / 10 + config.nTemp1Offset;
|
|
status.nHumid1 = (aht25.getHumidity() + 5) / 10 + config.nHumid1Offset;
|
|
break;
|
|
case TEMP_SENSOR_TYPE::AHT10_0x39:
|
|
status.nTemp1 = (aht10_0x39.getTemperature() + 5) / 10 + config.nTemp1Offset;
|
|
status.nHumid1 = (aht10_0x39.getHumidity() + 5) / 10 + config.nHumid1Offset;
|
|
break;
|
|
case TEMP_SENSOR_TYPE::NTC:
|
|
status.nTemp1 = ntc.getTemp() + config.nTemp1Offset;
|
|
status.nHumid1 = 0;
|
|
break;
|
|
case TEMP_SENSOR_TYPE::BLE_TUYA:
|
|
case TEMP_SENSOR_TYPE::BLE_XIAOMI_MIJIA:
|
|
status.nTemp1 = (ble.getTemp() + 5) / 10 + config.nTemp1Offset;
|
|
status.nHumid1 = (ble.getHumid() + 5) / 10 + config.nHumid1Offset;
|
|
if (ble.getBatteyLevel() > 30) status.nFlags &= ~FLAG_BLE_BATT;
|
|
else status.nFlags |= FLAG_BLE_BATT;
|
|
break;
|
|
default:
|
|
status.nTemp1 = 0;
|
|
status.nHumid1 = 0;
|
|
break;
|
|
}
|
|
|
|
switch (config.nTemp2SensorType) {
|
|
case TEMP_SENSOR_TYPE::AHT20:
|
|
case TEMP_SENSOR_TYPE::AHT2x:
|
|
status.nTemp2 = (aht25.getTemperature() + 5) / 10 + config.nTemp2Offset;
|
|
status.nHumid2 = (aht25.getHumidity() + 5) / 10 + config.nHumid2Offset;
|
|
break;
|
|
case TEMP_SENSOR_TYPE::AHT10_0x39:
|
|
status.nTemp2 = (aht10_0x39.getTemperature() + 5) / 10 + config.nTemp2Offset;
|
|
status.nHumid2 = (aht10_0x39.getHumidity() + 5) / 10 + config.nHumid2Offset;
|
|
break;
|
|
case TEMP_SENSOR_TYPE::NTC:
|
|
status.nTemp2 = ntc.getTemp() + config.nTemp2Offset;
|
|
status.nHumid2 = 0;
|
|
break;
|
|
case TEMP_SENSOR_TYPE::BLE_TUYA:
|
|
case TEMP_SENSOR_TYPE::BLE_XIAOMI_MIJIA:
|
|
status.nTemp2 = (ble.getTemp2() + 5) / 10 + config.nTemp2Offset;
|
|
status.nHumid2 = (ble.getHumid2() + 5) / 10 + config.nHumid2Offset;
|
|
if (ble.getBatteyLevel2() > 30) status.nFlags &= ~FLAG_BLE_BATT;
|
|
else status.nFlags |= FLAG_BLE_BATT;
|
|
break;
|
|
default:
|
|
status.nTemp2 = 0;
|
|
status.nHumid2 = 0;
|
|
break;
|
|
}
|
|
|
|
status.nTemp3 = ntc.getTemp() + config.nTemp3Offset;
|
|
}
|
|
|
|
// ==================================================================================
|
|
// Device Control Functions
|
|
// ==================================================================================
|
|
MY_IRAM_ATTR void controlAC1(short temp, unsigned long tick) {
|
|
uint16_t max = config.ac1.dutyMax * 10;
|
|
// Check Safety
|
|
if (temp >= config.nTempSafety) {
|
|
setHeater1Duty(0);
|
|
status.nHeater1Duty = 0;
|
|
return;
|
|
}
|
|
|
|
// Manual Control
|
|
if (status.nFlags & FLAG_MANUAL_HEATER1) {
|
|
if (status.nHeater1Duty > max) status.nHeater1Duty = max;
|
|
setHeater1Duty(status.nHeater1Duty);
|
|
return;
|
|
}
|
|
|
|
// Day & Night
|
|
bool bNight = isNight(g_nHour, g_nMinute);
|
|
if (!config.ac1.bDay && !bNight || !config.ac1.bNight && bNight) {
|
|
setHeater1Duty(0);
|
|
status.nHeater1Duty = 0;
|
|
return;
|
|
}
|
|
|
|
// No Sensor
|
|
if ((status.nTemp1 - config.nTemp1Offset) == 0 &&
|
|
(status.nHumid1 - config.nHumid1Offset) == 0) {
|
|
setHeater1Duty(0);
|
|
status.nHeater1Duty = 0;
|
|
return;
|
|
}
|
|
|
|
uint16_t setPoint;
|
|
int16_t duty;
|
|
|
|
//DPRINTF("AC1 Start: %d°C set: %d duty: %d\n", temp, setPoint, status.nHeater1Duty);
|
|
switch(config.ac1.nControlType) {
|
|
case CONTROL_TEMP_HEAT_PID:
|
|
{
|
|
setPoint = config.bNightControl && isNight(g_nHour, g_nMinute) ?
|
|
config.nTempTargetNight: config.nTempTarget;
|
|
duty = history.calculateDutyForTemp1(setPoint, temp, status.nHeater1Duty);
|
|
//DPRINTF("AC1: %d°C set: %d duty: %d --> %d\n", temp, setPoint, status.nHeater1Duty, duty);
|
|
status.nHeater1Duty = duty;
|
|
if (duty > 0)
|
|
duty = map(duty, 1, 10000, config.ac1.dutyMin * 10, config.ac1.dutyMax * 10);
|
|
setHeater1Duty(duty);
|
|
}
|
|
break;
|
|
case CONTROL_TEMP_COOL_PID:
|
|
case CONTROL_HUMIDITY_INC_PID:
|
|
case CONTROL_HUMIDITY_DEC_PID:
|
|
break;
|
|
default:
|
|
duty = controlDevice(&config.ac1, 0) * 10;
|
|
status.nHeater1Duty = duty;
|
|
if (duty > 0)
|
|
duty = map(duty, 1, 10000, config.ac1.dutyMin * 10, config.ac1.dutyMax * 10);
|
|
setHeater1Duty(duty);
|
|
break;
|
|
}
|
|
//DPRINTF("AC1 End: %d°C set: %d duty: %d\n", temp, setPoint, status.nHeater1Duty);
|
|
|
|
}
|
|
|
|
MY_IRAM_ATTR void controlAC2(short temp, unsigned long tick) {
|
|
uint16_t max = config.ac2.dutyMax * 10;
|
|
|
|
// Check Safety
|
|
if (temp >= config.nTempSafety) {
|
|
setHeater2Duty(0);
|
|
status.nHeater2Duty = 0;
|
|
//DPRINTLN("Safety Temp");
|
|
return;
|
|
}
|
|
|
|
// Manual Control
|
|
if (status.nFlags & FLAG_MANUAL_HEATER2) {
|
|
if (status.nHeater2Duty > max) status.nHeater2Duty = max;
|
|
setHeater2Duty(status.nHeater2Duty);
|
|
return;
|
|
}
|
|
|
|
// Day & Night
|
|
bool bNight = isNight(g_nHour, g_nMinute);
|
|
if (!config.ac2.bDay && !bNight || !config.ac2.bNight && bNight) {
|
|
setHeater2Duty(0);
|
|
status.nHeater2Duty = 0;
|
|
//DPRINTLN("Day/Night Reject");
|
|
return;
|
|
}
|
|
|
|
// No Sensor
|
|
if ((status.nTemp1 - config.nTemp1Offset) == 0 &&
|
|
(status.nHumid1 - config.nHumid1Offset) == 0) {
|
|
setHeater2Duty(0);
|
|
status.nHeater2Duty = 0;
|
|
//DPRINTLN("No Sensor");
|
|
return;
|
|
}
|
|
|
|
uint16_t setPoint;
|
|
int16_t duty;
|
|
switch(config.ac2.nControlType) {
|
|
case CONTROL_TEMP_HEAT_PID:
|
|
{
|
|
setPoint = config.bNightControl && isNight(g_nHour, g_nMinute) ?
|
|
config.nTempTargetNight: config.nTempTarget;
|
|
duty = history.calculateDutyForTemp1(setPoint, temp, status.nHeater2Duty);
|
|
//DPRINTF("AC2: %d°C set: %d duty: %d --> %d\n", temp, setPoint, status.nHeater2Duty, duty);
|
|
status.nHeater2Duty = duty;
|
|
if (duty > 0)
|
|
duty = map(duty, 1, 10000, config.ac1.dutyMin * 10, config.ac1.dutyMax * 10);
|
|
setHeater2Duty(duty);
|
|
}
|
|
break;
|
|
case CONTROL_TEMP_COOL_PID:
|
|
case CONTROL_HUMIDITY_INC_PID:
|
|
case CONTROL_HUMIDITY_DEC_PID:
|
|
break;
|
|
default:
|
|
status.nHeater2Duty = controlDevice(&config.ac2, 1) * 10;
|
|
if (status.nHeater2Duty > 0)
|
|
duty = map(status.nHeater2Duty, 1, 10000, config.ac2.dutyMin * 10, config.ac2.dutyMax * 10);
|
|
setHeater2Duty(duty);
|
|
break;
|
|
}
|
|
}
|
|
|
|
MY_IRAM_ATTR void controlMist(short humid, unsigned long tick) {
|
|
uint16_t duty = 0;
|
|
bool bNight = isNight(g_nHour, g_nMinute);
|
|
|
|
// Manual Control
|
|
if (status.nFlags & FLAG_MANUAL_MIST) {
|
|
duty = status.nMistDuty;
|
|
} else
|
|
// Day & Night
|
|
if (!config.mist.bDay && !bNight || !config.mist.bNight && bNight) {
|
|
duty = 0;
|
|
} else {
|
|
if (status.nTemp1 != 0 && status.nHumid1 != 0) {
|
|
switch(config.mist.nControlType) {
|
|
case CONTROL_HUMIDITY_INC_PID:
|
|
{
|
|
uint16_t setPoint;
|
|
setPoint = config.bNightControl && isNight(g_nHour, g_nMinute) ?
|
|
config.nHumidTargetNight: config.nHumidTarget;
|
|
duty = history.calculateMistDuty(setPoint, humid, status.nMistDuty);
|
|
//DPRINTF("Mist: %d%% set: %d duty: %d --> %d\n", humid, setPoint, status.nMistDuty, duty);
|
|
}
|
|
break;
|
|
case CONTROL_TEMP_HEAT_PID:
|
|
case CONTROL_TEMP_COOL_PID:
|
|
case CONTROL_HUMIDITY_DEC_PID:
|
|
duty = status.nMistDuty = 0;
|
|
break;
|
|
default:
|
|
duty = controlDevice(&config.mist, 2) * 10;
|
|
break;
|
|
}
|
|
} else {
|
|
duty = 0;
|
|
}
|
|
}
|
|
status.nMistDuty = duty;
|
|
|
|
// Control Duty
|
|
if (duty > PWM_OFF)
|
|
duty = map(duty, 1, 10000, config.mist.dutyMin, config.mist.dutyMax);
|
|
ledcWrite(PIN_MIST, duty);
|
|
}
|
|
|
|
MY_IRAM_ATTR void controlFan(short temp, unsigned long tick) {
|
|
if (status.nFlags & FLAG_MANUAL_FAN ) return;
|
|
|
|
status.nFanDuty = controlDevice(&config.fan, 3);
|
|
}
|
|
|
|
MY_IRAM_ATTR void controlMotor(short hour, short min, unsigned long tick) {
|
|
if (status.nFlags & FLAG_MANUAL_MOTOR ) return;
|
|
|
|
status.nMotorDuty = controlDevice(&config.motor, 4);
|
|
}
|
|
|
|
MY_IRAM_ATTR void controlLight(short hour, short min, unsigned long tick) {
|
|
if (status.nFlags & FLAG_MANUAL_LIGHT ) return;
|
|
|
|
status.nLightTargetDuty = controlDevice(&config.light, 5);
|
|
}
|
|
|
|
MY_IRAM_ATTR uint16_t controlDevice(DEVICE_PARAM_TYPE *pDevice, int idx) {
|
|
uint16_t duty;
|
|
switch(idx) {
|
|
case 0: duty = status.nHeater1Duty / 10; break; // AC1
|
|
case 1: duty = status.nHeater2Duty / 10; break; // AC2
|
|
case 2: duty = status.nMistDuty / 10; break; // Mist
|
|
case 3: duty = status.nFanDuty; break; // Fan
|
|
case 4: duty = status.nMotorDuty; break; // Motor
|
|
case 5: duty = status.nLightDuty; break; // Light
|
|
default: duty = 0; break;
|
|
}
|
|
|
|
bool bNight = isNight(g_nHour, g_nMinute);
|
|
if (!pDevice->bDay && !bNight || !pDevice->bNight && bNight)
|
|
return 0;
|
|
|
|
switch (pDevice->nControlType) {
|
|
case CONTROL_TEMP_HEAT:
|
|
if (status.nTemp1 < pDevice->tempLow) {
|
|
duty = pDevice->dutyOn;
|
|
}
|
|
else if (status.nTemp1 > pDevice->tempHigh) {
|
|
duty = pDevice->dutyOff;
|
|
}
|
|
break;
|
|
case CONTROL_TEMP_COOL:
|
|
if (status.nTemp1 > pDevice->tempHigh)
|
|
duty = pDevice->dutyOn;
|
|
else if (status.nTemp1 < pDevice->tempLow)
|
|
duty = pDevice->dutyOff;
|
|
break;
|
|
case CONTROL_HUMIDITY_DEC:
|
|
if (status.nHumid1 > pDevice->humidHigh)
|
|
duty = pDevice->dutyOn;
|
|
else if (status.nHumid1 < pDevice->humidLow)
|
|
duty = pDevice->dutyOff;
|
|
break;
|
|
case CONTROL_HUMIDITY_INC:
|
|
if (status.nHumid1 < pDevice->humidLow)
|
|
duty = pDevice->dutyOn;
|
|
else if (status.nHumid1 > pDevice->humidHigh)
|
|
duty = pDevice->dutyOff;
|
|
break;
|
|
case CONTROL_DAY_NIGHT:
|
|
{
|
|
duty = isNight(g_nHour, g_nMinute) ? pDevice->dutyNight : pDevice->dutyDay;
|
|
}
|
|
break;
|
|
case CONTROL_TIME:
|
|
{
|
|
uint16_t time = g_nHour * 60 + g_nMinute;
|
|
if (pDevice->timeBegin < pDevice->timeEnd) {
|
|
if (time >= pDevice->timeBegin && time < pDevice->timeEnd)
|
|
duty = pDevice->dutyOn;
|
|
else
|
|
duty = pDevice->dutyOff;
|
|
} else {
|
|
if (time >= pDevice->timeEnd && time < pDevice->timeBegin)
|
|
duty = pDevice->dutyOff;
|
|
else
|
|
duty = pDevice->dutyOn;
|
|
}
|
|
}
|
|
break;
|
|
case CONTROL_PERIOD:
|
|
{
|
|
static unsigned long periodStartTime[6] = {0}; // Tracks start of the current period (in seconds)
|
|
static unsigned long totalElapsedSeconds[6] = {0}; // Tracks elapsed time within the current period
|
|
static unsigned long lastCurrentTime[6] = {0xFFFFFFFF};
|
|
|
|
unsigned long time = g_nHour * 3600UL + g_nMinute * 60UL + g_nSecond;
|
|
unsigned long period = pDevice->periodPeriod * 60;
|
|
if (lastCurrentTime[idx] == 0xFFFFFFFF) {
|
|
lastCurrentTime[idx] = time;
|
|
}
|
|
if (time < lastCurrentTime[idx]) {
|
|
// Handle day rollover (23:59:59 -> 00:00:00)
|
|
totalElapsedSeconds[idx] += (24UL * 3600) + time - lastCurrentTime[idx]; // Normal time increment
|
|
} else {
|
|
totalElapsedSeconds[idx] += time - lastCurrentTime[idx]; // Normal time increment
|
|
}
|
|
lastCurrentTime[idx] = time;
|
|
|
|
// Reset totalElapsedSeconds when it exceeds the period
|
|
if (totalElapsedSeconds[idx] >= periodStartTime[idx] + period) {
|
|
totalElapsedSeconds[idx] -= period; // Wrap around to start a new period
|
|
periodStartTime[idx] = totalElapsedSeconds[idx]; // Reset the start time
|
|
duty = pDevice->dutyOn; // Start of the new period
|
|
}
|
|
|
|
// Turn off LED if the onDuration has passed within the current period
|
|
if (totalElapsedSeconds[idx] >= periodStartTime[idx] + pDevice->periodOn) {
|
|
duty = pDevice->dutyOff; // Start of the new period
|
|
} else {
|
|
duty = pDevice->dutyOn;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
duty = 0;
|
|
}
|
|
return duty;
|
|
}
|
|
|
|
MY_IRAM_ATTR void controlFanDuty() {
|
|
static uint16_t nFanDuty = 0;
|
|
|
|
if (nFanDuty != status.nFanDuty) {
|
|
if (nFanDuty == 0) {
|
|
// Start-up
|
|
nFanDuty = status.nFanDuty < config.fan.dutyStart ? config.fan.dutyStart : status.nFanDuty;
|
|
} else {
|
|
nFanDuty = status.nFanDuty;
|
|
}
|
|
|
|
int duty = 0;
|
|
if (nFanDuty > 0) {
|
|
duty = map(nFanDuty, 1, 1000, config.fan.dutyMin, config.fan.dutyMax);
|
|
}
|
|
ledcWrite(PIN_FAN, duty);
|
|
}
|
|
}
|
|
|
|
MY_IRAM_ATTR void controlMotorDuty() {
|
|
static uint16_t nMotorDuty = 0;
|
|
|
|
if (nMotorDuty != status.nMotorDuty) {
|
|
// Start-up
|
|
if (nMotorDuty == 0) {
|
|
nMotorDuty = status.nMotorDuty < config.motor.dutyStart ? config.motor.dutyStart : status.nMotorDuty;
|
|
} else {
|
|
nMotorDuty = status.nMotorDuty;
|
|
}
|
|
|
|
int duty = 0;
|
|
if (nMotorDuty > 0) {
|
|
duty = map(nMotorDuty, 1, 1000, config.motor.dutyMin, config.motor.dutyMax);
|
|
}
|
|
ledcWrite(PIN_MOTOR, duty);
|
|
}
|
|
}
|
|
|
|
MY_IRAM_ATTR void controlLightDuty() {
|
|
if (status.nLightDuty != status.nLightTargetDuty) {
|
|
int16_t step = status.nLightDuty < status.nLightTargetDuty ? 1 : -1;
|
|
status.nLightDuty += step;
|
|
int16_t duty = map(status.nLightDuty, 0, 1000, config.light.dutyMin, config.light.dutyMax);
|
|
ledcWrite(PIN_LIGHT, duty);
|
|
}
|
|
}
|
|
|
|
|
|
MY_IRAM_ATTR bool isNight(unsigned char currentHour, unsigned char currentMin) {
|
|
// Check if the current time is within the night range
|
|
|
|
if (config.nNightStartHour < config.nNightEndHour ||
|
|
(config.nNightStartHour == config.nNightEndHour && config.nNightStartMin < config.nNightEndMin)) {
|
|
// Case 1: Night starts and ends on the same day (e.g., 22:00 to 06:00)
|
|
if ((currentHour > config.nNightStartHour || (currentHour == config.nNightStartHour && currentMin >= config.nNightStartMin)) &&
|
|
(currentHour < config.nNightEndHour || (currentHour == config.nNightEndHour && currentMin <= config.nNightEndMin))) {
|
|
return true; // It's night time
|
|
}
|
|
} else {
|
|
// Case 2: Night crosses midnight (e.g., 22:00 to 06:00)
|
|
if ((currentHour > config.nNightStartHour || (currentHour == config.nNightStartHour && currentMin >= config.nNightStartMin)) ||
|
|
(currentHour < config.nNightEndHour || (currentHour == config.nNightEndHour && currentMin <= config.nNightEndMin))) {
|
|
return true; // It's night time
|
|
}
|
|
}
|
|
|
|
return false; // It's day time
|
|
}
|
|
|
|
// ======================================================================
|
|
//
|
|
// Utilities
|
|
//
|
|
// ======================================================================
|
|
MY_IRAM_ATTR char *printStatus(unsigned long tick, bool bLong) {
|
|
static char szStatus[256] = { 0 };
|
|
//if (config.bSendStatusSerial)
|
|
{
|
|
// Build Status String
|
|
char strHeat1[32], strHeat2[32], strMist[32], strLight1[32], strLight2[32];
|
|
strHeat1[0] = 0;
|
|
strHeat2[0] = 0;
|
|
strMist[0] = 0;
|
|
strLight1[0] = 0;
|
|
strLight2[0] = 0;
|
|
|
|
sprintf(strHeat1, "T(%2d.%d/%2d.%d°C H%2d.%d%%)",
|
|
status.nTemp1 / 10, status.nTemp1 % 10,
|
|
config.nTempTarget / 10, config.nTempTarget % 10,
|
|
status.nHeater1Duty / 100, status.nHeater1Duty % 100 );
|
|
|
|
sprintf(strMist, "H(%2d.%d/%2d.%d%% M%2d.%d%%)",
|
|
status.nHumid1 / 10, status.nHumid1 % 10,
|
|
config.nHumidTarget / 10, config.nHumidTarget % 10,
|
|
status.nMistDuty / 10, status.nMistDuty % 10);
|
|
|
|
sprintf(strLight1, "L(%2d.%d%%)", status.nLightDuty / 10, status.nLightDuty % 10);
|
|
|
|
sprintf(szStatus, "%s %s %s %s H(Kp %.2f, Kd %.2f) M(Kp %.2f, %.2f)",
|
|
printTime(bLong), strHeat1, strMist, strLight1,
|
|
history.getKpTemperature(), history.getKdTemperature(),
|
|
history.getKpHumidity(), history.getKdHumidity());
|
|
|
|
// Send out to clients
|
|
// ESP_LOGI(TAG_MAIN,"%s\n", szStatus);
|
|
}
|
|
return szStatus;
|
|
}
|
|
|
|
MY_IRAM_ATTR char *printTime(bool bLong) {
|
|
static char szBuff[48];
|
|
|
|
// Get current time from the system clock
|
|
time_t now;
|
|
time(&now); // Get current system time in seconds
|
|
struct tm *timeinfo = localtime(&now);
|
|
|
|
if (bLong) {
|
|
// Calculate uptime in seconds
|
|
/*
|
|
unsigned long uptime = now - timeManager.getFirstNTPTime();
|
|
|
|
// Calculate days, hours, minutes, seconds for uptime
|
|
int days = uptime / 86400; // Seconds in a day
|
|
uptime %= 86400;
|
|
int hours = uptime / 3600; // Seconds in an hour
|
|
uptime %= 3600;
|
|
int minutes = uptime / 60; // Seconds in a minute
|
|
int seconds = uptime % 60;
|
|
|
|
// Format the current time
|
|
char szDays[8], szHours[8];
|
|
if (days > 0)
|
|
sprintf(szDays, "%d ", days);
|
|
else
|
|
szDays[0] = 0;
|
|
|
|
if (hours > 0)
|
|
sprintf(szHours, "%d:", hours);
|
|
else
|
|
szHours[0] = 0;
|
|
*/
|
|
|
|
sprintf(szBuff, "%04d-%02d-%02d %02d:%02d:%02d",
|
|
timeinfo->tm_year + 1900, // Year
|
|
timeinfo->tm_mon + 1, // Month (0-11)
|
|
timeinfo->tm_mday, // Day of month
|
|
timeinfo->tm_hour, // Hour
|
|
timeinfo->tm_min, // Minute
|
|
timeinfo->tm_sec // Second
|
|
);
|
|
} else {
|
|
sprintf(szBuff, "%02d:%02d:%02d",
|
|
timeinfo->tm_hour, // Hour
|
|
timeinfo->tm_min, // Minute
|
|
timeinfo->tm_sec);
|
|
}
|
|
return szBuff;
|
|
}
|
|
|
|
inline void setTime() {
|
|
// Get time from System Clock - UTC
|
|
time(&now); // Get current system time in seconds (still in local time settings)
|
|
status.now = (uint32_t) now; // Store UTC time for external communication
|
|
|
|
// UTC time_r(&now, &timeinfo);
|
|
//gmtime_r(&now, &timeinfo);
|
|
//ESP_LOGI(TAG_MAIN,"Time - UTC: %2d:%02d:%02d ", timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec, g_nSecond);
|
|
|
|
// local time
|
|
now += config.m_nTimeOffset;
|
|
gmtime_r(&now, &timeinfo);
|
|
//ESP_LOGI(TAG_MAIN,"Local: %4d-%02d-%02d %2d:%02d:%02d (UTC%c%d)\n",
|
|
// g_nYear, g_nMonth, g_nDay,
|
|
// timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec,
|
|
// config.m_nTimeOffset >= 0 ? '+' : '-', (uint16_t)(config.m_nTimeOffset / 3600));
|
|
|
|
g_nSecond = static_cast<char>(timeinfo.tm_sec);
|
|
g_nMinute = static_cast<char>(timeinfo.tm_min);
|
|
g_nHour = static_cast<char>(timeinfo.tm_hour);
|
|
g_nDay = static_cast<char>(timeinfo.tm_mday);
|
|
g_nMonth = static_cast<char>(timeinfo.tm_mon) + 1;
|
|
g_nYear = timeinfo.tm_year + 1900;
|
|
|
|
}
|
|
|
|
inline void setZCD() {
|
|
// ZCD
|
|
status.zcdAC = zcdACCount;
|
|
zcdACCount = 0;
|
|
status.zcdLoad = zcdLoadCount;
|
|
zcdLoadCount = 0;
|
|
if (status.zcdAC < 118 || status.zcdAC > 122) {
|
|
status.nFlags |= FLAG_ZCD_AC;
|
|
}
|
|
else {
|
|
status.nFlags &= ~FLAG_ZCD_AC;
|
|
}
|
|
if (status.zcdLoad < 118 || status.zcdLoad > 122) {
|
|
status.nFlags |= FLAG_ZCD_LOAD;
|
|
}
|
|
else {
|
|
status.nFlags &= ~FLAG_ZCD_LOAD;
|
|
}
|
|
} |