#include #undef INADDR_NONE #include #include #include #include #include #include // For struct in_addr #include #include #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: if (pkt.op < ENUM_DEVICE_PARAM_TYPE::DEVICE_PARAM_TYPE_COUNT) { 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(); m_bReceiveConfigPending = false; if (m_bSaveReceivedConfig) { config.save(); m_bSaveReceivedConfig = false; DPRINTLN("[WiFi] Host - Config Received & saved"); } else { DPRINTLN("[WiFi] Host - Config Received."); } } DPRINTF("[WIFI] Host - ReceiveData: Data Received %dbytes!\r\n", m_nDataReceive_received); 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(); }