HCesp/WiFiHost.cpp

898 lines
30 KiB
C++

#include <lwip/sockets.h>
#undef INADDR_NONE
#include <lwip/netdb.h>
#include <WiFiServer.h>
#include <HTTPClient.h>
#include <WiFiClientSecure.h>
#include <esp_task_wdt.h>
#include <netinet/in.h> // For struct in_addr
#include <esp_task_wdt.h>
#include <esp_system.h>
#include "OTA.h"
#include "HermitCrab.h"
#include "Config.h"
#include "ConnectWiFi.h"
#include "TimeManager.h"
#include "History.h"
#include "zcd.h"
#include "AHT2x.h"
#include "WiFiHost.h"
//#include "UPnPClient.h"
#define TAG_WIFI_HOST "WiFi Host"
#define TCP_PACKET_SIZE_MAX 1460
//WiFiServer wifiServer(SERVER_PORT);
//WiFiClient wifiClient;
CWiFiHost host;
CONFIG_TYPE configCopy;
void CWiFiHost::Setup()
{
// UDP Packet
packetUDP = {
.sig1 = SIGNATURE1,
.m_nSize = sizeof(UDP_PACKET),
.m_nMessage = UDP_MESSAGE::MESSAGE_HEARTBEAT,
.m_nDeviceType = config.m_nDeviceType,
.m_nResetReason = RESET_REASON_CHIP_POWER_ON,
.m_nChipID = config.m_nChipId,
.m_nDeviceID = 0,
//.m_nVersion = (uint32_t)atoll(&HC__VERSION[2]),
.dwIPAddress = 0,
.m_nPort = UDP_PORT,
//.m_MACAddress = "",
//.m_sDeviceName = "",
.status = {0},
.sig2 = SIGNATURE2 };
packetUDP.m_nVersion = (uint32_t)atoll(&HC__VERSION[2]);
strncpy((char *)packetUDP.m_MACAddress, WiFi.macAddress().c_str(), 17);
packetUDP.m_MACAddress[17] = 0;
strcpy(packetUDP.m_sDeviceName, config.m_sDeviceName);
// TCP Packet
hostPacket = {
.sig1 = SIGNATURE1,
.len = sizeof(TCP_PACKET),
.cmd = ENUM_COMMAND::CMD_HELLO,
.op = OPERATION_MODE::MODE_WAITING,
.time = 0,
.status = {0},
.sig2 = SIGNATURE2 };
clientPacket = hostPacket;
//m_nPublicPort = config.m_nPublicPort;
wifiServer = WiFiServer(SERVER_PORT, 1);
//wifiExternal = WiFiServer(m_nPublicPort, 1);
//wifiStatus = WIFI_NOT_CONNECTED;
m_nDataSend_sent = 0;
m_nDataReceive_size = 0;
m_pDataSend_data = nullptr;
m_nDataReceive_received = 0;
m_nDataReceive_size = 0;
m_pDataReceive_data = nullptr;
externalServerIP = IPAddress((uint32_t) 0UL);
//sockfd = -1;
//connectStartTime = 0;
//isConnecting = false;
// Operation
m_nMode = MODE_WAITING;
m_bHelloSent = false;
m_bSendHistoryPending = false;
m_nLastReceivedTime =
m_nLastHeartBeatSentTime =
m_nLastUDPBroadcastTime = millis();
m_bClientConnected = false;
//m_dwPublicIP = 0;
wifiClient.stop();
if (isWiFiConnected())
{
// UPnP Client
// {
// uint32_t ip = WiFi.gatewayIP();
// uint16_t port = m_nPublicPort;
// //CUpnpClient upnp;
// //if (upnp.registerUPnP(&ip, &port)) {
// // status.nFlags |= FLAG_UPNP;
// //} else {
// status.nFlags &= ~FLAG_UPNP;
// //}
// if (ip != 0)
// m_dwPublicIP = ip;
// if (port != m_nPublicPort) {
// config.m_nPublicPort = port;
// config.save();
// m_nPublicPort = port;
// }
// }
// Server
wifiServer.begin(SERVER_PORT, 1);
//wifiExternal.begin(m_nPublicPort, 1);
m_bClientConnected = false;
// UDP
packetUDP.dwIPAddress = WiFi.localIP();
udpLocal.begin(UDP_PORT);
udpExternal.begin(UDP_EXTERNAL_PORT);
if (m_cExternalServerIPAddress == IPAddress((uint32_t) 0l)) {
if (WiFi.hostByName("visionsoft.kr", m_cExternalServerIPAddress)) {
DPRINTF("WiFi - ExternalServer IP resolved as \"%s\"\n", m_cExternalServerIPAddress.toString().c_str());
} else {
m_cExternalServerIPAddress = IPAddress((uint32_t) 0);
DPRINTF("WiFi - ExternalServer IP NOT resolved\n");
}
}
}
}
MY_IRAM_ATTR void CWiFiHost::Stop() {
CloseConnection();
// Stop server
wifiServer.stop();
//wifiExternal.stop();
m_bClientConnected = false;
// Stop Client
if (wifiClient)
wifiClient.stop();
// Stop UDP
udpLocal.stop(); // or udpLocal.end(); depending on your preference
udpExternal.stop(); // or udpExternal.end(); depending on your preference
}
MY_IRAM_ATTR
void CWiFiHost::CloseConnection()
{
if (wifiClient && wifiClient.connected()) wifiClient.stop();
m_bClientConnected = false;
m_nMode = MODE_WAITING;
}
IRAM_ATTR void CWiFiHost::Loop(unsigned long clock)
{
if (!isWiFiConnected()) return;
// --- State: WAITING (Accepting new clients) ---
if (m_nMode == MODE_WAITING) {
// If we think we are connected but the object is dead, reset
if (m_bClientConnected) {
wifiClient.stop();
m_bClientConnected = false;
}
// Try accepting from internal, then external if internal fails
wifiClient = wifiServer.accept();
//if (!wifiClient) wifiClient = wifiExternal.accept();
if (wifiClient && wifiClient.connected()) {
ESP_LOGI(TAG_WIFI_HOST, "Host: Connection Accepted");
wifiClient.setNoDelay(true);
m_nLastReceivedTime = clock;
m_bClientConnected = true;
m_bHelloSent = false;
m_nMode = MODE_PACKET;
ledcWrite(PIN_LED_WIFI, PWM_FULL * 39 / 40);
}
return;
}
// --- Global Safety Check for all other modes ---
// If we lose connection mid-process, jump back to WAITING immediately
if (!wifiClient || !wifiClient.connected()) {
m_bClientConnected = false;
m_nMode = MODE_WAITING;
ledcWrite(PIN_LED_WIFI, PWM_FULL / 10 );
return;
}
// --- Main State Machine ---
switch (m_nMode) {
case MODE_PACKET:
CheckClient(clock);
// Timeout Check (60 seconds)
if (clock - m_nLastReceivedTime > 60000) {
ESP_LOGW(TAG_WIFI_HOST, "Host: Timeout - Dropping Connection");
wifiClient.stop();
m_bClientConnected = false;
m_nMode = MODE_WAITING;
}
// Heartbeat Pulse (Every 1 second)
else if (clock - m_nLastHeartBeatSentTime >= 1000) {
SendHeartBeat();
m_nLastHeartBeatSentTime = clock;
}
break;
case MODE_SEND:
// SendData returns true only when the CURRENT buffer is empty
if (SendData(clock)) {
// Check if there is a second part of the history ring buffer
if (m_bSendHistoryPending && m_nPendingHistoryCount > 0) {
ESP_LOGI(TAG_WIFI_HOST, "WiFi Host: Preparing History Part 2");
// Setup the next chunk
SendData(history.getRingData2(), sizeof(STATUS_TYPE) * m_nPendingHistoryCount);
// Clear flags so we don't loop here forever
m_bSendHistoryPending = false;
m_nPendingHistoryCount = 0;
// We stay in MODE_SEND. The next Loop() turn will start sending Part 2.
} else {
// All data (including history parts) is done
m_nMode = MODE_PACKET;
}
}
break;
case MODE_RECV:
if (ReceiveData(clock)) {
m_nMode = MODE_PACKET;
}
break;
}
}
MY_IRAM_ATTR void CWiFiHost::SendHeartBeat(unsigned long clock) {
if (!isWiFiConnected()) {
//ESP_LOGI(TAG_WIFI_HOST,"WiFiHost - SendHeartBeat() called while not connected!");
return;
}
// Send Heartbeats to external server and to local devices
if (clock - m_nLastUDPBroadcastTime > 1000) {
static int count = 55;
// UDP Heartbeat
if (++count >= 60) {
// External Heartbeat
UDP_CONFIG_TYPE pktConfig;
pktConfig.udp = packetUDP;
pktConfig.udp.m_nPort = 3939;
pktConfig.udp.dwIPAddress = (uint32_t)WiFi.localIP();
pktConfig.udp.status = status;
if (((uint32_t)m_cExternalServerIPAddress) != (uint32_t)0l)
udpExternal.beginPacket(m_cExternalServerIPAddress, (uint16_t)UDP_EXTERNAL_PORT);
else
udpExternal.beginPacket("visionsoft.kr", (uint16_t)UDP_EXTERNAL_PORT);
if (config.bConfigSaved) {
// Config is save locally. Save it on cloud
config.bConfigSaved = false;
pktConfig.udp.m_nMessage = MESSAGE_CONFIG_SAVE;
pktConfig.con = config;
udpExternal.write((uint8_t*)&pktConfig, sizeof(pktConfig));
ESP_LOGI(TAG_WIFI_HOST,"HeartBeat Packet *Config Save* sent out to external Server");
} else {
// Send only UDP packet
udpExternal.write((uint8_t*)&(pktConfig.udp), sizeof(UDP_PACKET));
}
udpExternal.endPacket();
count = 0;
} else if (!m_bClientConnected) {
// Local AP broadcast
packetUDP.m_nMessage = UDP_MESSAGE::MESSAGE_HEARTBEAT;
packetUDP.dwIPAddress = WiFi.localIP();
packetUDP.m_nPort = SERVER_PORT;
strcpy(packetUDP.m_sCompanyName, COMPANY_NAME);
strcpy(packetUDP.m_sService, SERVICE_NAME);
udpLocal.beginPacket((IPAddress)0xFFFFFFFF, (uint16_t)UDP_PORT);
udpLocal.write((uint8_t*)&packetUDP, sizeof(UDP_PACKET));
udpLocal.endPacket();
}
m_nLastUDPBroadcastTime = clock;
}
}
MY_IRAM_ATTR void CWiFiHost::MonitorUDP() {
// Listen for UDP External port
IPAddress ip;
int size = udpExternal.parsePacket();
if (size >= sizeof(packetUDP)) {
if ((udpExternal.read((char *) &packetUDP, sizeof(packetUDP)) == sizeof(packetUDP)) &&
packetUDP.sig1 == SIGNATURE1 &&
packetUDP.sig2 == SIGNATURE2 &&
packetUDP.m_nChipID == config.m_nChipId) {
switch (packetUDP.m_nMessage) {
case MESSAGE_IP: // Extenal IP and Port set
// if (packetUDP.m_nChipID == config.m_nChipId) {
// m_dwPublicIP = packetUDP.dwIPAddress;
// m_nPublicPort = packetUDP.m_nPort;
// ip = IPAddress(m_dwPublicIP);
// ESP_LOGI(TAG_WIFI_HOST,"External IP(%s) and Port(%d)\n", ip.toString().c_str(), m_nPublicPort);
// } else {
// ESP_LOGI(TAG_WIFI_HOST,"External Server Response for other device Received\n");
// }
break;
case MESSAGE_CONFIG_SEND:
if (size == sizeof(UDP_PACKET) + sizeof(CONFIG_STRUCT)) {
if (udpExternal.read((char*) &configCopy, sizeof(configCopy))) {
ESP_LOGI(TAG_WIFI_HOST,"CONFIG received from the external DB server\n");
}
} else {
char *buffer[256];
while((size = udpExternal.parsePacket()) > 0) {
// Discard any leftover data
udpExternal.read((char*) &buffer, sizeof(buffer));
}
}
break;
case MESSAGE_QUERY_IP:
// if (m_nMode == MODE_WAITING) {
// // Get sender's IP and port
// externalServerIP = udpExternal.remoteIP();
// uint16_t senderPort = udpExternal.remotePort();
// ESP_LOGI(TAG_WIFI_HOST,"External Server - MESSAGE_QUERY_IP from %s:%d\n", externalServerIP.toString().c_str(), senderPort);
// m_nMode = MODE_EXTERNAL_SERVER;
// }
break;
case MESSAGE_RESET:
Restart();
break;
default:
break;
}
}
}
}
typedef union {
float f;
uint32_t u;
} float_bits;
// Optimized for range +/- 0.0001 to +/- 3000
IRAM_ATTR uint16_t f32tof16(float f32) {
float_bits fb;
fb.f = f32;
// Extract sign, move to bit 15
uint32_t sign = (fb.u >> 16) & 0x8000;
// Shift exponent from 8-bit (bias 127) to 5-bit (bias 15)
// 0x7f800000 masks the exponent
uint32_t exp = ((((fb.u & 0x7f800000) >> 23) - 127 + 15) & 0x1f) << 10;
// Truncate 23-bit mantissa to 10-bit
uint32_t mant = (fb.u >> 13) & 0x03ff;
return (uint16_t)(sign | exp | mant);
}
IRAM_ATTR float f16tof32(uint16_t f16) {
float_bits fb;
// Shift sign to bit 31
uint32_t sign = (uint32_t)(f16 & 0x8000) << 16;
// Shift exponent from 5-bit (bias 15) back to 8-bit (bias 127)
uint32_t exp = (((f16 >> 10) & 0x1f) - 15 + 127) << 23;
// Shift 10-bit mantissa back to 23-bit
uint32_t mant = (f16 & 0x03ff) << 13;
fb.u = sign | exp | mant;
return fb.f;
}
IRAM_ATTR void CWiFiHost::CheckClient(unsigned long clock)
{
// 1. Quick exit if no data is ready to be read
int available = wifiClient.available();
if (available < sizeof(TCP_PACKET)) return;
static TCP_PACKET cpkt;
// 2. Peek or Read the packet
// We use readBytes because we already verified 'available' >= size
int count = wifiClient.readBytes((uint8_t*)&cpkt, sizeof(TCP_PACKET));
if (count == sizeof(TCP_PACKET)) {
// 3. Validation Logic
bool sigMatch = (cpkt.sig1 == SIGNATURE1 && cpkt.sig2 == SIGNATURE2);
bool lenMatch = (cpkt.len == sizeof(TCP_PACKET));
if (sigMatch && lenMatch) {
ProcessPacket(cpkt); // Cleanly dispatched to sub-handlers now
m_nLastReceivedTime = clock;
} else {
// 4. Recovery Logic: Instead of a blocking while-loop,
// clear the socket buffer and wait for the next loop cycle.
DPRINTF("[Host] ErrPacket CMD(%d) OP(%d) len(%d) sig(%s)\n", cpkt.cmd, cpkt.op, cpkt.len, sigMatch ? "OK" : "fail");
while(wifiClient.available() > 0) {
wifiClient.read(); // Efficiently dump the garbage
}
}
}
}
IRAM_ATTR void CWiFiHost::ProcessPacket(TCP_PACKET& pkt)
{
// Jump Table definition (GCC Labels-as-values)
static const void* jump_table[] = {
[0] = &&L_DEFAULT,
// Connection & Control
[CMD_HEARTBEAT] = &&L_CMD_HEARTBEAT,
[CMD_HELLO] = &&L_CMD_HELLO,
[CMD_DROP_CONNECTION] = &&L_CMD_DROP_CONNECTION,
[CMD_RESET_REASON] = &&L_CMD_RESET_REASON,
[CMD_RESTART] = &&L_CMD_RESTART,
[CMD_SEND_HISTORY] = &&L_CMD_SEND_HISTORY,
[CMD_RESET_SENSOR] = &&L_CMD_RESET_SENSOR,
[CMD_RESET_DISPLAY] = &&L_CMD_RESET_DISPLAY,
// Config
[CMD_INIT_CONFIG] = &&L_CMD_INIT_CONFIG,
[CMD_LOAD_CONFIG] = &&L_CMD_LOAD_CONFIG,
[CMD_SAVE_CONFIG] = &&L_CMD_SAVE_CONFIG,
[CMD_SEND_CONFIG] = &&L_CMD_SEND_CONFIG,
[CMD_RECV_CONFIG] = &&L_CMD_RECV_CONFIG,
[CMD_SET_DEVICE_PARAM] = &&L_CMD_SET_DEVICE_PARAM,
[CMD_GET_DEVICE_PARAM] = &&L_CMD_GET_DEVICE_PARAM,
// Control
[CMD_SET_CONTROL] = &&L_CMD_SET_CONTROL,
[CMD_GET_CONTROL] = &&L_CMD_GET_CONTROL,
// Set
[CMD_SET_TEMP_TARGET] = &&L_CMD_SET_TEMP_TARGET,
[CMD_SET_TEMP_TARGET_NIGHT] = &&L_CMD_SET_TEMP_TARGET_NIGHT,
[CMD_SET_HUMID_TARGET] = &&L_CMD_SET_HUMID_TARGET,
[CMD_SET_SENSOR_OFFSET] = &&L_CMD_SET_SENSOR_OFFSET,
[CMD_SET_HEATER1_DUTY] = &&L_CMD_SET_HEATER1_DUTY,
[CMD_SET_MANUAL_HEATER1] = &&L_CMD_SET_MANUAL_HEATER1,
[CMD_SET_HEATER2_DUTY] = &&L_CMD_SET_HEATER2_DUTY,
[CMD_SET_MANUAL_HEATER2] = &&L_CMD_SET_MANUAL_HEATER2,
[CMD_SET_MIST_DUTY] = &&L_CMD_SET_MIST_DUTY,
[CMD_SET_MANUAL_MIST] = &&L_CMD_SET_MANUAL_MIST,
[CMD_SET_FAN_DUTY] = &&L_CMD_SET_FAN_DUTY,
[CMD_SET_MANUAL_FAN] = &&L_CMD_SET_MANUAL_FAN,
[CMD_SET_MOTOR_DUTY] = &&L_CMD_SET_MOTOR_DUTY,
[CMD_SET_MANUAL_MOTOR] = &&L_CMD_SET_MANUAL_MOTOR,
[CMD_SET_LIGHT_DUTY] = &&L_CMD_SET_LIGHT_DUTY,
[CMD_SET_MANUAL_LIGHT] = &&L_CMD_SET_MANUAL_LIGHT,
[CMD_SET_TIME_NIGHT] = &&L_CMD_SET_TIME_NIGHT,
[CMD_SET_WIFI_CLIENT_DISPLAY] = &&L_CMD_SET_WIFI_CLIENT_DISPLAY,
// PID
[CMD_INIT_PID_PARAM] = &&L_CMD_INIT_PID_PARAM,
[CMD_LOAD_PID_PARAM] = &&L_CMD_LOAD_PID_PARAM,
[CMD_SAVE_PID_PARAM] = &&L_CMD_SAVE_PID_PARAM,
[CMD_SET_PID] = &&L_CMD_SET_PID,
[CMD_GET_PID] = &&L_CMD_GET_PID
};
// Bounds check to prevent illegal memory access
if (pkt.cmd >= CMD_COUNT || pkt.cmd <= 0) goto L_DEFAULT;
DPRINTF("[HOST] CMD(%d) OP(%d) LEN(%d) U[%d][%d][%d][%d]\n",
pkt.cmd, pkt.op, pkt.len, pkt.u16[0], pkt.u16[1], pkt.u16[2], pkt.u16[3]);
// Direct jump
goto *jump_table[pkt.cmd];
L_CMD_HEARTBEAT:
m_nLastReceivedTime = millis();
return;
L_CMD_HELLO:
DPRINTLN("[WiFi] HELLO received");
pkt.u16[0] = SIGNATURE1;
pkt.n16[1] = (int16_t)sizeof(CONFIG_TYPE);
pkt.u16[2] = SIGNATURE2;
SendPacket(pkt);
SendData((uint8_t*)&config, sizeof(CONFIG_TYPE));
m_bHelloSent = true;
return;
L_CMD_DROP_CONNECTION:
wifiClient.stop();
m_bClientConnected = false;
m_nMode = MODE_WAITING;
DPRINTLN("[WiFI] Host - Client requests to DROP CONNECTION");
return;
L_CMD_RESET_REASON:
pkt.n16[0] = (int16_t)esp_reset_reason();
SendPacket(pkt);
return;
L_CMD_RESTART:
if (pkt.u16[0] == SIGNATURE1 && pkt.u16[1] == SIGNATURE2) {
if (pkt.op)
config.save();
pkt.cmd = CMD_DROP_CONNECTION;
SendPacket(pkt);
Restart();
}
return;
L_CMD_SEND_HISTORY:
if (pkt.u16[0] == SIGNATURE1 && pkt.u16[1] == SIGNATURE2) {
int16_t count = history.getRingCount();
if (count > 0 ) {
pkt.op = count;
SendPacket(pkt);
int16_t size = history.getRingSize();
if (count < size) {
SendData(history.getRingData1(), sizeof(STATUS_TYPE) * count);
} else {
int count1st = size - history.getRingTail();
SendData(history.getRingData1(), sizeof(STATUS_TYPE) * count1st);
m_bSendHistoryPending = true;
m_nPendingHistoryCount = history.getRingHead();
}
}
}
return;
L_CMD_RESET_SENSOR:
aht25.setScanFlag(true);
aht10_0x39.setScanFlag(true);
return;
L_CMD_RESET_DISPLAY:
return;
L_CMD_INIT_CONFIG:
if (pkt.u16[0] == SIGNATURE1 && pkt.u16[1] == sizeof(CONFIG_STRUCT) && pkt.u16[2] == SIGNATURE2) {
config.init();
config.save();
}
return;
L_CMD_LOAD_CONFIG:
if (pkt.u16[0] == SIGNATURE1 && pkt.u16[1] == sizeof(CONFIG_STRUCT) && pkt.u16[2] == SIGNATURE2) {
config.load();
history.loadPID();
}
return;
L_CMD_SAVE_CONFIG:
if (pkt.u16[0] == SIGNATURE1 && pkt.u16[1] == sizeof(CONFIG_STRUCT) && pkt.u16[2] == SIGNATURE2) {
config.save();
}
return;
L_CMD_RECV_CONFIG:
if (pkt.u16[0] == SIGNATURE1 && pkt.u16[1] == sizeof(CONFIG_STRUCT) && pkt.u16[2] == SIGNATURE2) {
ReceiveData((uint8_t *)&configCopy, sizeof(CONFIG_TYPE));
m_bReceiveConfigPending = true;
m_bSaveReceivedConfig = pkt.op ? true : false;
DPRINTLN("[WiFi] Host - Receive Config initiated...");
}
return;
L_CMD_SEND_CONFIG:
if (pkt.u16[0] == SIGNATURE1 && pkt.u16[1] == sizeof(CONFIG_STRUCT) && pkt.u16[2] == SIGNATURE2) {
SendPacket(pkt);
SendData((const uint8_t *)&config, sizeof(config));
DPRINTLN("[WiFi] Host - Send Config initiated...");
}
return;
L_CMD_SET_DEVICE_PARAM:
config.deviceParamArray[pkt.op] = pkt.device;
return;
L_CMD_GET_DEVICE_PARAM:
pkt.device = config.deviceParamArray[pkt.op];
return;
L_CMD_GET_CONTROL:
pkt.by[0] = config.bSmartControl ? 0xFF : 0x00;
pkt.by[1] = config.bNightControl ? 0xFF : 0x00;
pkt.by[2] = config.bControlTemperature ? 0xFF : 0x00;
pkt.by[3] = config.bControlHumidity ? 0xFF : 0x00;
SendPacket(pkt);
return;
L_CMD_SET_CONTROL:
config.bSmartControl = pkt.by[0] ? true : false;
config.bNightControl = pkt.by[1] ? true : false;
config.bControlTemperature = pkt.by[2] ? true : false;
config.bControlHumidity = pkt.by[3] ? true : false;
return;
L_CMD_SET_TEMP_TARGET:
config.nTempTarget = pkt.u16[0];
config.nTempTargetNight = pkt.u16[1];
return;
L_CMD_SET_TEMP_TARGET_NIGHT:
config.nTempTargetNight = pkt.u16[0];
return;
L_CMD_SET_HUMID_TARGET:
config.nHumidTarget = pkt.u16[0];
return;
L_CMD_SET_SENSOR_OFFSET:
config.nTemp1Offset = pkt.n16[0];
config.nTemp2Offset = pkt.n16[1];
config.nTemp3Offset = pkt.n16[2];
config.nHumid1Offset = pkt.n16[3];
config.nHumid2Offset = pkt.n16[4];
//config.nHumid3Offset = pkt.n16[5]; T3 = NTC No Humidity
return;
L_CMD_SET_HEATER1_DUTY:
status.nHeater1Duty = pkt.u16[0];
L_CMD_SET_MANUAL_HEATER1:
if (pkt.u16[1]) status.nFlags |= FLAG_MANUAL_HEATER1;
else status.nFlags &= ~FLAG_MANUAL_HEATER1;
if (status.nHeater1Duty == 0) setHeater1Duty(0);
return;
L_CMD_SET_HEATER2_DUTY:
status.nHeater2Duty = pkt.u16[0];
L_CMD_SET_MANUAL_HEATER2:
if (pkt.u16[1]) status.nFlags |= FLAG_MANUAL_HEATER2;
else status.nFlags &= ~FLAG_MANUAL_HEATER2;
if (status.nHeater2Duty == 0) setHeater2Duty(0);
return;
L_CMD_SET_MIST_DUTY:
status.nMistDuty = pkt.u16[0];
L_CMD_SET_MANUAL_MIST:
if (pkt.u16[1]) status.nFlags |= FLAG_MANUAL_MIST;
else status.nFlags &= ~FLAG_MANUAL_MIST;
return;
L_CMD_SET_FAN_DUTY:
status.nFanDuty = pkt.u16[0];
L_CMD_SET_MANUAL_FAN:
if (pkt.u16[1]) status.nFlags |= FLAG_MANUAL_FAN;
else status.nFlags &= ~FLAG_MANUAL_FAN;
return;
L_CMD_SET_MOTOR_DUTY:
status.nMotorDuty = pkt.u16[0];
L_CMD_SET_MANUAL_MOTOR:
if (pkt.u16[1]) status.nFlags |= FLAG_MANUAL_MOTOR;
else status.nFlags &= ~FLAG_MANUAL_MOTOR;
return;
L_CMD_SET_LIGHT_DUTY:
status.nLightTargetDuty = pkt.u16[0];
L_CMD_SET_MANUAL_LIGHT:
if (pkt.u16[1]) status.nFlags |= FLAG_MANUAL_LIGHT;
else status.nFlags &= ~FLAG_MANUAL_LIGHT;
return;
L_CMD_SET_TIME_NIGHT:
config.nNightStartHour = pkt.u16[0];
config.nNightStartMin = pkt.u16[1];
config.nNightEndHour = pkt.u16[2];
config.nNightEndMin = pkt.u16[3];
return;
L_CMD_SET_WIFI_CLIENT_DISPLAY:
config.m_nDisplayTempHigh = pkt.n16[0];
config.m_nDisplayTempLow = pkt.n16[1];
config.m_nDisplayTime = pkt.n16[2];
config.m_fShowRealTime = pkt.n16[3];
config.m_fShowHistory = pkt.n16[4];
return;
L_CMD_INIT_PID_PARAM:
L_CMD_SAVE_PID_PARAM:
history.savePID();
return;
L_CMD_LOAD_PID_PARAM:
history.loadPID();
return;
L_CMD_SET_PID:
config.Kp_Temp1 = f16tof32(pkt.u16[0]);
config.Kd_Temp1 = f16tof32(pkt.u16[1]);
config.LR_Temp1 = f16tof32(pkt.u16[2]);
config.Kp_Temp2 = f16tof32(pkt.u16[3]);
config.Kd_Temp2 = f16tof32(pkt.u16[4]);
config.LR_Temp2 = f16tof32(pkt.u16[5]);
config.Kp_Temp3 = f16tof32(pkt.u16[6]);
config.Kd_Temp3 = f16tof32(pkt.u16[7]);
config.LR_Temp3 = f16tof32(pkt.u16[8]);
config.Kp_Humidity = f16tof32(pkt.u16[9]);
config.Kd_Humidity = f16tof32(pkt.u16[10]);
config.LR_Humidity = f16tof32(pkt.u16[11]);
config.Kp_Humid2 = f16tof32(pkt.u16[12]);
config.Kd_Humid2 = f16tof32(pkt.u16[13]);
config.LR_Humid2 = f16tof32(pkt.u16[14]);
config.Kp_Humid3 = f16tof32(pkt.u16[15]);
config.Kd_Humid3 = f16tof32(pkt.u16[16]);
config.LR_Humid3 = f16tof32(pkt.u16[17]);
history.loadPID();
return;
L_CMD_GET_PID:
pkt.u16[0] = f32tof16(config.Kp_Temp1);
pkt.u16[1] = f32tof16(config.Kd_Temp1);
pkt.u16[2] = f32tof16(config.LR_Temp1);
pkt.u16[3] = f32tof16(config.Kp_Temp2);
pkt.u16[4] = f32tof16(config.Kd_Temp2);
pkt.u16[5] = f32tof16(config.LR_Temp2);
pkt.u16[6] = f32tof16(config.Kp_Temp3);
pkt.u16[7] = f32tof16(config.Kd_Temp3);
pkt.u16[8] = f32tof16(config.LR_Temp3);
pkt.u16[9] = f32tof16(config.Kp_Humidity);
pkt.u16[10] = f32tof16(config.Kd_Humidity);
pkt.u16[11] = f32tof16(config.LR_Humidity);
pkt.u16[12] = f32tof16(config.Kp_Humid2);
pkt.u16[13] = f32tof16(config.Kd_Humid2);
pkt.u16[14] = f32tof16(config.LR_Humid2);
pkt.u16[15] = f32tof16(config.Kp_Humid3);
pkt.u16[16] = f32tof16(config.Kd_Humid3);
pkt.u16[17] = f32tof16(config.LR_Humid3);
SendPacket(pkt);
return;
L_DEFAULT:
DPRINTF("[WiFi] Host - Unknown Packet: %d\n", pkt.cmd);
return;
}
IRAM_ATTR int CWiFiHost::SendPacket(TCP_PACKET& pkt)
{
size_t sent = 0;
if (m_bClientConnected && wifiClient && wifiClient.connected())
{
sent = wifiClient.write((char*)&pkt, sizeof(TCP_PACKET));
}
return sent;
}
IRAM_ATTR size_t CWiFiHost::SendData(const uint8_t* data, size_t size) {
if (data != nullptr) {
m_nMode = MODE_SEND;
m_pDataSend_data = (char *) data;
m_nDataSend_size = size;
m_nDataSend_sent = 0;
DPRINTF("[HOST] Host - SendData(size: %d)\n", size);
return size;
}
return 0;
}
// This function sends a small "chunk" then returns false.
// It returns true ONLY when the entire buffer is finished.
IRAM_ATTR bool CWiFiHost::SendData(unsigned long clock)
{
if (!m_pDataSend_data || m_nDataSend_size == 0) return true;
// Calculate how much is left
size_t remaining = m_nDataSend_size - m_nDataSend_sent;
// Send a "Chunk" (e.g., 512 bytes).
// Sending too much at once blocks the CPU.
// Sending too little makes the WiFi overhead too high.
size_t chunkSize = (remaining > 512) ? 512 : remaining;
size_t written = wifiClient.write((const uint8_t*)(m_pDataSend_data + m_nDataSend_sent), chunkSize);
if (written > 0) {
m_nDataSend_sent += written;
}
// If we aren't done, return false to stay in MODE_SEND for the next Loop()
if (m_nDataSend_sent < m_nDataSend_size) {
return false;
}
// Done! Reset pointers
m_pDataSend_data = nullptr;
m_nDataSend_size = 0;
m_nDataSend_sent = 0;
return true;
}
IRAM_ATTR size_t CWiFiHost::ReceiveData(uint8_t* data, size_t size)
{
m_nMode = MODE_RECV;
m_pDataReceive_data = (char *) data;
m_nDataReceive_size = size;
m_nDataReceive_received = 0;
return size;
}
MY_IRAM_ATTR bool CWiFiHost::ReceiveData(unsigned long clock)
{
// Receive Data
size_t nSize = 0;
if (m_nDataReceive_received < m_nDataReceive_size)
{
if (m_bClientConnected && wifiClient && wifiClient.connected()) {
int16_t count = m_nDataReceive_size - m_nDataReceive_received;
if (count > TCP_PACKET_SIZE_MAX) count = TCP_PACKET_SIZE_MAX;
nSize = wifiClient.readBytes(&m_pDataReceive_data[m_nDataReceive_received], count);
if (nSize > 0) {
m_nLastReceivedTime = clock;
m_nDataReceive_received += nSize;
}
} else {
m_nMode = MODE_WAITING;
m_pDataReceive_data = nullptr;
m_nDataReceive_size = 0;
m_nDataReceive_received = 0;
ESP_LOGI(TAG_WIFI_HOST," SendData: Connection lost - reset receiveData()!");
return true;
}
}
// Check for any pending action
if (m_nDataReceive_received == m_nDataReceive_size) {
if (m_bReceiveConfigPending && m_pDataReceive_data == (char *)&configCopy) {
config = configCopy;
history.loadPID();
DPRINTLN("[WiFi] Host - Config Received");
m_bReceiveConfigPending = false;
if (m_bSaveReceivedConfig) {
config.save();
m_bSaveReceivedConfig = false;
}
}
DPRINTLN("[WIFI] Host - ReceiveData: Data Receive Completed!");
return true;
}
if (nSize <= 0 && clock - m_nLastReceivedTime > 5000) {
DPRINTLN("[WIFI] Host - ReceiveData: TimeOut Abort!");
return true;
}
return false;
}
IRAM_ATTR void CWiFiHost::SendHeartBeat() {
if (m_bHelloSent) {
hostPacket.cmd = CMD_HEARTBEAT;
time_t now = time(NULL); // Get current time in seconds
status.now = (uint32_t) time(NULL);
//status.uptime = now - timeManager.getFirstNTPTime();
hostPacket.status = status;
SendPacket(hostPacket);
}
}
MY_IRAM_ATTR
void CWiFiHost::Restart() {
if (isWiFiConnected()) {
if (m_bClientConnected && m_bClientConnected && wifiClient && wifiClient.connected()) {
hostPacket.cmd = CMD_DROP_CONNECTION;
SendPacket(hostPacket);
}
}
vTaskDelay(500/portTICK_PERIOD_MS);
// stop all network sockets
Stop(); // Stop Sockets
vTaskDelay(50/portTICK_PERIOD_MS);
// Turn Off
setHeater1Duty(0);
setHeater2Duty(0);
ledcWrite(PIN_MIST, 0);
ledcWrite(PIN_FAN, 0);
ledcWrite(PIN_MOTOR, 0);
ledcWrite(PIN_LIGHT, 0);
vTaskDelay(50/portTICK_PERIOD_MS);
config.statusSave = status;
config.save();
vTaskDelay(50/portTICK_PERIOD_MS);
ESP.restart();
}