ZCD using gpio based Interrupt
This commit is contained in:
parent
1dd537b5d0
commit
ef05f6f997
154
CommSerial.cpp
154
CommSerial.cpp
|
|
@ -1,154 +0,0 @@
|
||||||
#include "HermitCrab.h"
|
|
||||||
#include "Config.h"
|
|
||||||
#include "History.h"
|
|
||||||
#include "zcd.h"
|
|
||||||
|
|
||||||
const char *strDeviceType[] = {
|
|
||||||
"None",
|
|
||||||
"Test",
|
|
||||||
"ESP8266",
|
|
||||||
"ON_OFF",
|
|
||||||
"ZCD",
|
|
||||||
"CAM",
|
|
||||||
"Beta",
|
|
||||||
"Beta_BLE",
|
|
||||||
"End"
|
|
||||||
};
|
|
||||||
|
|
||||||
MY_IRAM_ATTR void checkSerial(unsigned long tick)
|
|
||||||
{
|
|
||||||
static char buffer[256];
|
|
||||||
static short idx = 0;
|
|
||||||
static unsigned long val;
|
|
||||||
|
|
||||||
while (Serial.available() > 0) {
|
|
||||||
if (idx > 254) {
|
|
||||||
idx = 0;
|
|
||||||
ESP_LOGI(TAG,"SrialHost: Buffer OverFlow");
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer[idx] = Serial.read();
|
|
||||||
if (buffer[idx] == '\n') {
|
|
||||||
if (idx >= 1) {
|
|
||||||
buffer[idx] = 0;
|
|
||||||
|
|
||||||
switch(buffer[0])
|
|
||||||
{
|
|
||||||
case 'T': // Temp Target
|
|
||||||
if (idx > 1) {
|
|
||||||
val = atoi(&buffer[1]);
|
|
||||||
if (val >= 25 && val <= 35) {
|
|
||||||
config.nTempTarget = val * 10;
|
|
||||||
} else if (val >= 250 && val <= 350) {
|
|
||||||
config.nTempTarget = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Serial.printf("%s SerialSet: Temp Target %d.%d°C Duty(%.2f%%)\n",
|
|
||||||
printStatus(tick, false), config.nTempTarget / 10, config.nTempTarget % 10,
|
|
||||||
status.nHeater1Duty / 100.0f);
|
|
||||||
break;
|
|
||||||
case 't': // Temp Target Night
|
|
||||||
if (idx > 1) {
|
|
||||||
val = atoi(&buffer[1]);
|
|
||||||
if (val >= 25 && val <= 35) {
|
|
||||||
config.nTempTargetNight = val * 10;
|
|
||||||
} else if (val >= 250 && val <= 350) {
|
|
||||||
config.nTempTargetNight = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Serial.printf("%s SerialSet: Temp Target Night %d.%d°C Duty(%.2f%%)\n",
|
|
||||||
printStatus(tick, false),
|
|
||||||
config.nTempTargetNight / 10, config.nTempTargetNight % 10,
|
|
||||||
status.nHeater1Duty/ 100.0f);
|
|
||||||
break;
|
|
||||||
case 'H': // Humidity Target
|
|
||||||
case 'h':
|
|
||||||
if (idx > 1) {
|
|
||||||
val = atoi(&buffer[1]);
|
|
||||||
if (val >= 20 && val <= 95) {
|
|
||||||
config.nHumidTarget = val * 10;
|
|
||||||
} else if (val >= 200 && val <= 950) {
|
|
||||||
config.nHumidTarget = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Serial.printf("%s SerialSet: Humid Target(%d.%d%%) Mist %s (Duty: %d)\n",
|
|
||||||
printStatus(tick),
|
|
||||||
config.nHumidTarget / 10, config.nHumidTarget % 10,
|
|
||||||
status.nMistDuty > 0 ? "ON" : "OFF",
|
|
||||||
status.nMistDuty);
|
|
||||||
break;
|
|
||||||
case 'M': // MistOn time
|
|
||||||
case 'm': // MistDelay time
|
|
||||||
if (idx > 1) {
|
|
||||||
val = atoi(&buffer[1]);
|
|
||||||
if (val >= 0 && val <= 1023) {
|
|
||||||
status.nMistDuty = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Serial.printf("%s SerialSet: Mist %s (Duty: %d)\n",
|
|
||||||
printStatus(tick), status.nMistDuty > 0 ? "ON" : "OFF", status.nMistDuty);
|
|
||||||
break;
|
|
||||||
case 'l': //Light1
|
|
||||||
case 'L':
|
|
||||||
if (idx > 2) {
|
|
||||||
val = atoi(&buffer[1]);
|
|
||||||
if (val >= PWM_OFF && val <= PWM_FULL) {
|
|
||||||
status.nLightTargetDuty = val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Serial.printf("%s SerialSet: Light1 %s (%d --> %d)\n",
|
|
||||||
printStatus(tick), status.nLightDuty > 0 ? "ON" : "OFF",
|
|
||||||
status.nLightDuty, status.nLightTargetDuty);
|
|
||||||
break;
|
|
||||||
case 'd': // Display Sensor
|
|
||||||
if (idx < 2) {
|
|
||||||
bShowSensor = !bShowSensor;
|
|
||||||
} else {
|
|
||||||
val = atoi(&buffer[1]);
|
|
||||||
bShowSensor = val == 0 ? false : true;
|
|
||||||
}
|
|
||||||
Serial.printf("%s SerialSet: DisplaySensor %s\n", printStatus(tick), bShowSensor ? "On" : "Off");
|
|
||||||
break;
|
|
||||||
case 'p': // Print
|
|
||||||
case 'P':
|
|
||||||
break;
|
|
||||||
case 's': // Save Config
|
|
||||||
case 'S':
|
|
||||||
history.savePID();
|
|
||||||
config.save();
|
|
||||||
Serial.printf("%s Config Saved\n", printStatus(tick));
|
|
||||||
break;
|
|
||||||
case 'Y': // Device Type
|
|
||||||
case 'y':
|
|
||||||
if (idx > 1) {
|
|
||||||
val = atoi(&buffer[1]);
|
|
||||||
char *sz = NULL;
|
|
||||||
if (val > TYPE_NONE && val < TYPE_DEVICE_END) {
|
|
||||||
config.m_nDeviceType = val;
|
|
||||||
Serial.printf("%s SeriaSet: DeviceType %s\n", printStatus(tick), strDeviceType[val]);
|
|
||||||
} else {
|
|
||||||
Serial.printf("%s SerialSet: Invalid DeviceType\n");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case 'Z':
|
|
||||||
case 'z':
|
|
||||||
if (idx > 1) {
|
|
||||||
val = atoi(&buffer[1]);
|
|
||||||
if (val >= 0 && val <= 10000) {
|
|
||||||
//ESP_LOGI(TAG,"%s SerialSet: Set Heater Duty %.1f%%\n", printStatus(tick), dutyPercent);
|
|
||||||
status.nHeater1Duty = val;
|
|
||||||
setHeater1Duty(status.nHeater1Duty);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear Buffer
|
|
||||||
idx = 0;
|
|
||||||
} else {
|
|
||||||
idx++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
#ifndef DEBUG
|
#ifndef DEBUG
|
||||||
#define DEBUG 1 // Set to 0 to disable debug output
|
#define DEBUG 1 // Set to 0 to disable debug output
|
||||||
#endif
|
#endif
|
||||||
//#undef DEBUG
|
#undef DEBUG
|
||||||
|
|
||||||
|
|
||||||
//#define BLE_DEBUG
|
//#define BLE_DEBUG
|
||||||
|
|
@ -219,7 +219,6 @@ typedef struct STATUS_STRUCT {
|
||||||
|
|
||||||
char *printStatus(unsigned long tick, bool bLong = false);
|
char *printStatus(unsigned long tick, bool bLong = false);
|
||||||
char *printTime(bool bLong = false);
|
char *printTime(bool bLong = false);
|
||||||
void checkSerial(unsigned long tick);
|
|
||||||
void checkWiFi(unsigned long tick);
|
void checkWiFi(unsigned long tick);
|
||||||
void checkWiFiHost(unsigned long tick);
|
void checkWiFiHost(unsigned long tick);
|
||||||
|
|
||||||
|
|
|
||||||
2
OTA.cpp
2
OTA.cpp
|
|
@ -17,7 +17,7 @@
|
||||||
// OTA
|
// OTA
|
||||||
//
|
//
|
||||||
// ==============================================================
|
// ==============================================================
|
||||||
const char *HC__VERSION = "2026041814";
|
const char *HC__VERSION = "20260418016";
|
||||||
#define UPDATE_PORT ((uint16_t) 443)
|
#define UPDATE_PORT ((uint16_t) 443)
|
||||||
const char *url = "visionsoft.kr";
|
const char *url = "visionsoft.kr";
|
||||||
const char *uri = "/sc/pages/firmware_download.php";
|
const char *uri = "/sc/pages/firmware_download.php";
|
||||||
|
|
|
||||||
|
|
@ -1,491 +0,0 @@
|
||||||
#include <HTTPClient.h>
|
|
||||||
#include "HermitCrab.h"
|
|
||||||
#include "UPnpClient.h"
|
|
||||||
#include "Config.h"
|
|
||||||
#include "WiFiHost.h"
|
|
||||||
#define TAG_UPNP "UPnP"
|
|
||||||
|
|
||||||
|
|
||||||
bool CUpnpClient::registerUPnP(uint32_t *pip, uint16_t *pport) {
|
|
||||||
routerIP = *pip; // Gateway IP address
|
|
||||||
publicPort = *pport; // host.m_nPublicPort = config.m_nPublicPort
|
|
||||||
publicIP = 0UL;
|
|
||||||
bool bSuccess = false;
|
|
||||||
ESP_LOGI(TAG_UPNP,"UPnP %s(%D)\n", routerIP.toString().c_str(), publicPort);
|
|
||||||
|
|
||||||
// Step 1 - Discover UPnP service.
|
|
||||||
if (discoverUPnP()) {
|
|
||||||
// Step 2 - Check if mapping already exists
|
|
||||||
if (!(bSuccess = requestPortMappingEntry())) {
|
|
||||||
// Step 3 - Request new mapping
|
|
||||||
bSuccess = requestPortForwarding();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG_UPNP," UPnP discovery failed.");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bSuccess) {
|
|
||||||
// Extract external IP
|
|
||||||
requestExternalIP();
|
|
||||||
ESP_LOGI(TAG_UPNP," Public IP(Port): %s(%d)\n", publicIP.toString().c_str(), publicPort);
|
|
||||||
|
|
||||||
|
|
||||||
// Extract external port assigned by UPnP
|
|
||||||
// requestExternalPort();
|
|
||||||
// ESP_LOGI(TAG_UPNP,"Assigned External Port: %d\n", publicPort);
|
|
||||||
|
|
||||||
*pport = publicPort; // External server port
|
|
||||||
*pip = publicIP; // External IP address
|
|
||||||
return true;
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG_UPNP," UPnP PortForwarding failed.");
|
|
||||||
}
|
|
||||||
return bSuccess;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CUpnpClient::discoverUPnP() {
|
|
||||||
//ESP_LOGI(TAG_UPNP,"\n\n** Sending SSDP discovery request...");
|
|
||||||
WiFiUDP udp;
|
|
||||||
bool ret = false;
|
|
||||||
|
|
||||||
// SSDP M-SEARCH Request
|
|
||||||
const char *ssdpRequest =
|
|
||||||
"M-SEARCH * HTTP/1.1\r\n"
|
|
||||||
"HOST: 239.255.255.250:1900\r\n"
|
|
||||||
"MAN: \"ssdp:discover\"\r\n"
|
|
||||||
"MX: 3\r\n"
|
|
||||||
"ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
|
|
||||||
"\r\n";
|
|
||||||
|
|
||||||
udp.beginMulticast(SSDP_MULTICAST_IP, SSDP_PORT);
|
|
||||||
udp.beginPacket(SSDP_MULTICAST_IP, SSDP_PORT);
|
|
||||||
udp.write((const uint8_t *)ssdpRequest, strlen(ssdpRequest)); // Fix length
|
|
||||||
udp.endPacket();
|
|
||||||
|
|
||||||
unsigned long startTime = millis();
|
|
||||||
while (millis() - startTime < SEARCH_TIMEOUT) {
|
|
||||||
int packetSize = udp.parsePacket();
|
|
||||||
if (packetSize > 0) {
|
|
||||||
udp.read(buffer, sizeof(buffer) - 1);
|
|
||||||
buffer[packetSize] = '\0';
|
|
||||||
|
|
||||||
//ESP_LOGI(TAG_UPNP,"SSDP response received:");
|
|
||||||
//ESP_LOGI(TAG_UPNP,buffer);
|
|
||||||
|
|
||||||
// Check if the response contains "ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1"
|
|
||||||
char *stField = strstr(buffer, "ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1");
|
|
||||||
if (stField) {
|
|
||||||
// Extract router's UPnP service URL
|
|
||||||
char *location = strstr(buffer, "LOCATION: ");
|
|
||||||
if (location) {
|
|
||||||
location += 9; // Move past "LOCATION: "
|
|
||||||
while (*location == ' ') location++; // skip blanks
|
|
||||||
char *end = strchr(location, '\r');
|
|
||||||
if (end) {
|
|
||||||
*end = '\0';
|
|
||||||
//ESP_LOGI(TAG_UPNP,"Router UPnP URL: %s\n", location);
|
|
||||||
routerLocation = String(location);
|
|
||||||
|
|
||||||
// Extract IP and Port from the Location URL
|
|
||||||
int firstColonPos = routerLocation.indexOf(':'); // Find the first colon (for the protocol)
|
|
||||||
int secondColonPos = routerLocation.indexOf(':', firstColonPos + 1); // Find the second colon (IP:PORT)
|
|
||||||
|
|
||||||
if (secondColonPos != -1) {
|
|
||||||
routerIPString = routerLocation.substring(routerLocation.indexOf("://") + 3, secondColonPos); // Extract IP
|
|
||||||
routerPort = routerLocation.substring(secondColonPos + 1, routerLocation.indexOf('/', secondColonPos)).toInt(); // Extract Port
|
|
||||||
}
|
|
||||||
|
|
||||||
routerIP.fromString(routerIPString);
|
|
||||||
//ESP_LOGI(TAG_UPNP,"Router UPnP IP(Port): %s(%d)\n", routerIP.toString().c_str(), routerPort);
|
|
||||||
|
|
||||||
String xml = fetchUPnPDescription(routerLocation);
|
|
||||||
if (!xml.isEmpty()) {
|
|
||||||
// ESP_LOGI(TAG_UPNP,xml);
|
|
||||||
// ESP_LOGI(TAG_UPNP,"\n--- End Of XML ----\n");
|
|
||||||
|
|
||||||
// Parse the XML and get the controlURL
|
|
||||||
// String parseXML(const String &xml);
|
|
||||||
controlURL = parseXML(xml);
|
|
||||||
if (!controlURL.isEmpty()) {
|
|
||||||
ret = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
udp.stop();
|
|
||||||
|
|
||||||
if (!ret) {
|
|
||||||
DPRINTLN("No SSDP response from UPnP device.");
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
String CUpnpClient::fetchUPnPDescription(const String &location) {
|
|
||||||
HTTPClient http;
|
|
||||||
WiFiClient client;
|
|
||||||
|
|
||||||
//ESP_LOGI(TAG_UPNP,"Fetching UPnP XML from: \"%s\"\n", location.c_str());
|
|
||||||
|
|
||||||
http.begin(client, location);
|
|
||||||
int httpCode = http.GET();
|
|
||||||
|
|
||||||
if (httpCode > 0) {
|
|
||||||
//ESP_LOGI(TAG_UPNP,"HTTP Response Code: %d\n", httpCode);
|
|
||||||
if (httpCode == HTTP_CODE_OK) {
|
|
||||||
String xmlContent = http.getString();
|
|
||||||
http.end();
|
|
||||||
return xmlContent;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
//ESP_LOGI(TAG_UPNP,"HTTP Request failed, error: %s\n", http.errorToString(httpCode).c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
http.end();
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
String CUpnpClient::parseXML(const String &xml) {
|
|
||||||
// Locate the WANIPConnection service
|
|
||||||
int servicePos = xml.indexOf("<serviceType>urn:schemas-upnp-org:service:WANIPConnection:1</serviceType>");
|
|
||||||
if (servicePos == -1) {
|
|
||||||
ESP_LOGI(TAG_UPNP,"WANIPConnection service not found in XML.");
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find the start of the controlURL tag within the <service> block
|
|
||||||
int controlStart = xml.indexOf("<controlURL>", servicePos);
|
|
||||||
if (controlStart == -1) {
|
|
||||||
ESP_LOGI(TAG_UPNP,"controlURL not found in service block.");
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
controlStart += 12; // Move past "<controlURL>"
|
|
||||||
|
|
||||||
// Find the closing tag
|
|
||||||
int controlEnd = xml.indexOf("</controlURL>", controlStart);
|
|
||||||
if (controlEnd == -1) {
|
|
||||||
ESP_LOGI(TAG_UPNP,"Malformed XML: Missing </controlURL>.");
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
// Extract and clean the controlURL
|
|
||||||
String controlURL = xml.substring(controlStart, controlEnd);
|
|
||||||
controlURL.trim(); // Remove any spaces or newlines
|
|
||||||
|
|
||||||
//ESP_LOGI(TAG_UPNP,"Extracted controlURL: %s\n", controlURL.c_str());
|
|
||||||
return controlURL;
|
|
||||||
}
|
|
||||||
|
|
||||||
int CUpnpClient::sendSoapRequest(const char *request, char *response, size_t responseSize) {
|
|
||||||
WiFiClient client;
|
|
||||||
if (!client.connect(routerIP, routerPort)) { // UPnP uses port 1900 for SOAP requests
|
|
||||||
ESP_LOGI(TAG_UPNP,"Failed to connect to router: %s\n", routerIP.toString().c_str());
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
client.print("POST /control?WANIPConn1 HTTP/1.1\r\n");
|
|
||||||
client.print("Host: ");
|
|
||||||
client.print(routerIP);
|
|
||||||
client.print("\r\n");
|
|
||||||
client.print("Content-Type: text/xml; charset=\"utf-8\"\r\n");
|
|
||||||
client.print("SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#GetSpecificPortMappingEntry\"\r\n");
|
|
||||||
client.print("Content-Length: ");
|
|
||||||
client.print(strlen(request));
|
|
||||||
client.print("\r\n\r\n");
|
|
||||||
client.print(request);
|
|
||||||
|
|
||||||
unsigned long startMillis = millis();
|
|
||||||
while (!client.available() && millis() - startMillis < 5000) {
|
|
||||||
delay(10); // Wait for response
|
|
||||||
}
|
|
||||||
|
|
||||||
int index = 0;
|
|
||||||
while (client.available() && index < responseSize - 1) {
|
|
||||||
response[index++] = client.read();
|
|
||||||
}
|
|
||||||
response[index] = '\0';
|
|
||||||
|
|
||||||
client.stop();
|
|
||||||
return index;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CUpnpClient::requestPortMappingEntry() {
|
|
||||||
ESP_LOGI(TAG_UPNP,"** Requesting UPnP Port Mapping Entry...");
|
|
||||||
char soapRequest[512];
|
|
||||||
char mappingName[32];
|
|
||||||
sprintf(mappingName, "HC_%04X", publicPort);
|
|
||||||
|
|
||||||
snprintf(soapRequest, sizeof(soapRequest),
|
|
||||||
"<?xml version=\"1.0\"?>"
|
|
||||||
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
|
|
||||||
"s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
|
||||||
"<s:Body>"
|
|
||||||
"<u:GetSpecificPortMappingEntry xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">"
|
|
||||||
"<NewRemoteHost></NewRemoteHost>"
|
|
||||||
"<NewExternalPort>%d</NewExternalPort>"
|
|
||||||
"<NewProtocol>TCP</NewProtocol>"
|
|
||||||
"<NewInternalClient>%s</NewInternalClient>"
|
|
||||||
"<NewPortMappingDescription>%s</NewPortMappingDescription>"
|
|
||||||
"</u:GetSpecificPortMappingEntry>"
|
|
||||||
"</s:Body>"
|
|
||||||
"</s:Envelope>",
|
|
||||||
publicPort,
|
|
||||||
WiFi.localIP().toString().c_str(),
|
|
||||||
mappingName);
|
|
||||||
|
|
||||||
char response[1024];
|
|
||||||
int responseLen = sendSoapRequest(soapRequest, response, sizeof(response));
|
|
||||||
|
|
||||||
if (responseLen > 0) {
|
|
||||||
if (strstr(response, mappingName)) {
|
|
||||||
//ESP_LOGI(TAG_UPNP," Port mapping already exists.");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ESP_LOGI(TAG_UPNP," Port mapping not found.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CUpnpClient::requestPortForwarding() {
|
|
||||||
// Sending port-forwarding request to the router's external IP or gateway
|
|
||||||
ESP_LOGI(TAG_UPNP,"** Requesting UPnP Port Forwarding...");
|
|
||||||
|
|
||||||
if (controlURL.isEmpty()) {
|
|
||||||
ESP_LOGI(TAG_UPNP,"Error: controlURL is empty.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure controlURL is correctly formatted (handle absolute/relative cases)
|
|
||||||
String postURL = controlURL;
|
|
||||||
if (postURL.startsWith("http://") || postURL.startsWith("https://")) {
|
|
||||||
int pathStart = postURL.indexOf('/', 8); // Skip "http://"
|
|
||||||
if (pathStart != -1) {
|
|
||||||
postURL = postURL.substring(pathStart); // Extract path only
|
|
||||||
ESP_LOGI(TAG_UPNP,"PostURL: \"%s\"\n", postURL.c_str());
|
|
||||||
} else {
|
|
||||||
ESP_LOGI(TAG_UPNP,"Invalid controlURL format.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// XML body
|
|
||||||
char xmlBody[800];
|
|
||||||
snprintf(xmlBody, sizeof(xmlBody),
|
|
||||||
"<?xml version=\"1.0\"?>"
|
|
||||||
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
|
||||||
"<s:Body><u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">"
|
|
||||||
"<NewRemoteHost></NewRemoteHost>"
|
|
||||||
"<NewExternalPort>%d</NewExternalPort>"
|
|
||||||
"<NewProtocol>TCP</NewProtocol>"
|
|
||||||
"<NewInternalPort>%d</NewInternalPort>"
|
|
||||||
"<NewInternalClient>%s</NewInternalClient>"
|
|
||||||
"<NewEnabled>1</NewEnabled>"
|
|
||||||
"<NewPortMappingDescription>HC_%04X</NewPortMappingDescription>"
|
|
||||||
"<NewLeaseDuration>0</NewLeaseDuration>"
|
|
||||||
"</u:AddPortMapping></s:Body></s:Envelope>",
|
|
||||||
publicPort, publicPort,
|
|
||||||
WiFi.localIP().toString().c_str(),
|
|
||||||
publicPort);
|
|
||||||
//ESP_LOGI(TAG_UPNP,"XML Body: ");
|
|
||||||
//ESP_LOGI(TAG_UPNP,xmlBody);
|
|
||||||
|
|
||||||
|
|
||||||
// Calculate the correct content length
|
|
||||||
int contentLength = strlen(xmlBody);
|
|
||||||
|
|
||||||
snprintf(buffer, sizeof(buffer),
|
|
||||||
"POST %s HTTP/1.1\r\n"
|
|
||||||
"Host: %s:%d\r\n"
|
|
||||||
"Content-Type: text/xml; charset=\"utf-8\"\r\n"
|
|
||||||
"SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#AddPortMapping\"\r\n"
|
|
||||||
"Content-Length: %d\r\n"
|
|
||||||
"Connection: Close\r\n"
|
|
||||||
"\r\n"
|
|
||||||
"%s", // Append XML body after headers
|
|
||||||
controlURL.c_str(),
|
|
||||||
routerIP.toString().c_str(), routerPort,
|
|
||||||
contentLength,
|
|
||||||
xmlBody);
|
|
||||||
|
|
||||||
//ESP_LOGI(TAG_UPNP,"==== HTTP Request ===\n\n%s\n\n--- End Of HTTP ---\n", buffer);
|
|
||||||
|
|
||||||
// Connect and send
|
|
||||||
WiFiClient client;
|
|
||||||
if (!client.connect(routerIP, routerPort)) {
|
|
||||||
ESP_LOGI(TAG_UPNP,"Failed to connect to the router. %s:%d\n", routerIP.toString().c_str(), routerPort);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
client.print(buffer);
|
|
||||||
|
|
||||||
// Read response
|
|
||||||
String response;
|
|
||||||
uint32_t timeout = millis() + 5000;
|
|
||||||
while (client.available() == 0) {
|
|
||||||
if (millis() > timeout) {
|
|
||||||
ESP_LOGI(TAG_UPNP,"Router did not respond to port mapping request.");
|
|
||||||
client.stop();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (client.available()) {
|
|
||||||
response += client.readString();
|
|
||||||
}
|
|
||||||
client.stop();
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool CUpnpClient::requestExternalIP() {
|
|
||||||
//ESP_LOGI(TAG_UPNP,"\n\nRequesting External IP via UPnP...");
|
|
||||||
|
|
||||||
// XML Request Body
|
|
||||||
char xmlBody[300];
|
|
||||||
snprintf(xmlBody, sizeof(xmlBody),
|
|
||||||
"<?xml version=\"1.0\"?>"
|
|
||||||
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
|
||||||
"<s:Body>"
|
|
||||||
"<u:GetExternalIPAddress xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\"/>"
|
|
||||||
"</s:Body>"
|
|
||||||
"</s:Envelope>");
|
|
||||||
|
|
||||||
int contentLength = strlen(xmlBody);
|
|
||||||
|
|
||||||
// HTTP Request
|
|
||||||
snprintf(buffer, sizeof(buffer),
|
|
||||||
"POST %s HTTP/1.1\r\n"
|
|
||||||
"Host: %s:%d\r\n"
|
|
||||||
"Content-Type: text/xml; charset=\"utf-8\"\r\n"
|
|
||||||
"SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#GetExternalIPAddress\"\r\n"
|
|
||||||
"Content-Length: %d\r\n"
|
|
||||||
"Connection: Close\r\n"
|
|
||||||
"\r\n"
|
|
||||||
"%s",
|
|
||||||
controlURL.c_str(),
|
|
||||||
routerIP.toString().c_str(), routerPort,
|
|
||||||
contentLength,
|
|
||||||
xmlBody);
|
|
||||||
|
|
||||||
// Connect and Send
|
|
||||||
WiFiClient client;
|
|
||||||
if (!client.connect(routerIP, routerPort)) {
|
|
||||||
DPRINTLN("Failed to connect to router for External IP request.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
client.print(buffer);
|
|
||||||
|
|
||||||
// Read Response
|
|
||||||
String response;
|
|
||||||
uint32_t timeout = millis() + 5000;
|
|
||||||
while (client.available() == 0) {
|
|
||||||
if (millis() > timeout) {
|
|
||||||
DPRINTLN("Router did not respond to External IP request.");
|
|
||||||
client.stop();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (client.available()) {
|
|
||||||
response += client.readString();
|
|
||||||
}
|
|
||||||
client.stop();
|
|
||||||
|
|
||||||
//ESP_LOGI(TAG_UPNP,"External IP Response:");
|
|
||||||
//ESP_LOGI(TAG_UPNP,response);
|
|
||||||
|
|
||||||
// Extract External IP from XML
|
|
||||||
char *sz = (char *)(response.c_str());
|
|
||||||
char *extIP = strstr(sz, "<NewExternalIPAddress>");
|
|
||||||
if (extIP) {
|
|
||||||
extIP += 22;
|
|
||||||
char *end = strchr(extIP, '<');
|
|
||||||
if (end) *end = '\0';
|
|
||||||
|
|
||||||
publicIP = IPAddress(extIP);
|
|
||||||
DPRINTF("UPnP - External IP: %s\n", extIP);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
DPRINTLN("Failed to extract External IP.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CUpnpClient::requestExternalPort() {
|
|
||||||
//ESP_LOGI(TAG_UPNP,"\n\nRequesting External Port via UPnP...");
|
|
||||||
|
|
||||||
// XML Request Body for getting External Port
|
|
||||||
char xmlBody[300];
|
|
||||||
snprintf(xmlBody, sizeof(xmlBody),
|
|
||||||
"<?xml version=\"1.0\"?>"
|
|
||||||
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
|
|
||||||
"<s:Body>"
|
|
||||||
"<u:GetExternalPort xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\"/>"
|
|
||||||
"</s:Body>"
|
|
||||||
"</s:Envelope>");
|
|
||||||
|
|
||||||
int contentLength = strlen(xmlBody);
|
|
||||||
|
|
||||||
// HTTP Request to get External Port
|
|
||||||
snprintf(buffer, sizeof(buffer),
|
|
||||||
"POST %s HTTP/1.1\r\n"
|
|
||||||
"Host: %s:%d\r\n"
|
|
||||||
"Content-Type: text/xml; charset=\"utf-8\"\r\n"
|
|
||||||
"SOAPAction: \"urn:schemas-upnp-org:service:WANIPConnection:1#GetExternalPort\"\r\n"
|
|
||||||
"Content-Length: %d\r\n"
|
|
||||||
"Connection: Close\r\n"
|
|
||||||
"\r\n"
|
|
||||||
"%s",
|
|
||||||
controlURL.c_str(),
|
|
||||||
routerIP.toString().c_str(), routerPort,
|
|
||||||
contentLength,
|
|
||||||
xmlBody);
|
|
||||||
|
|
||||||
// Connect and Send
|
|
||||||
WiFiClient client;
|
|
||||||
if (!client.connect(routerIP, routerPort)) {
|
|
||||||
ESP_LOGI(TAG_UPNP,"Failed to connect to router for External Port request.");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
client.print(buffer);
|
|
||||||
|
|
||||||
// Read Response
|
|
||||||
String response;
|
|
||||||
uint32_t timeout = millis() + 5000;
|
|
||||||
while (client.available() == 0) {
|
|
||||||
if (millis() > timeout) {
|
|
||||||
ESP_LOGI(TAG_UPNP,"Router did not respond to External Port request.");
|
|
||||||
client.stop();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
while (client.available()) {
|
|
||||||
response += client.readString();
|
|
||||||
}
|
|
||||||
client.stop();
|
|
||||||
|
|
||||||
//ESP_LOGI(TAG_UPNP,"External Port Response:");
|
|
||||||
//ESP_LOGI(TAG_UPNP,response);
|
|
||||||
|
|
||||||
// Extract External Port from XML
|
|
||||||
char *sz = (char *)(response.c_str());
|
|
||||||
char *extPort = strstr(sz, "<NewExternalPort>");
|
|
||||||
if (extPort) {
|
|
||||||
extPort += 17;
|
|
||||||
char *end = strchr(extPort, '<');
|
|
||||||
if (end) *end = '\0';
|
|
||||||
|
|
||||||
publicPort = atoi(extPort);
|
|
||||||
ESP_LOGI(TAG_UPNP,"External Port: %d\n", publicPort);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
ESP_LOGI(TAG_UPNP,"Failed to extract External Port.");
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
|
|
@ -1,37 +0,0 @@
|
||||||
#include <WiFi.h>
|
|
||||||
#include <WiFiUdp.h>
|
|
||||||
#define SSDP_MULTICAST_IP IPAddress("239.255.255.250")
|
|
||||||
#define SSDP_PORT 1900
|
|
||||||
#define UPNP_HTTP_PORT 5000 // Most routers use 5000, but it can vary
|
|
||||||
#define SEARCH_TIMEOUT 5000 // 2 seconds timeout
|
|
||||||
|
|
||||||
class CUpnpClient {
|
|
||||||
private:
|
|
||||||
IPAddress routerIP;
|
|
||||||
String routerIPString;
|
|
||||||
uint16_t routerPort;
|
|
||||||
|
|
||||||
IPAddress publicIP;
|
|
||||||
uint16_t publicPort;
|
|
||||||
|
|
||||||
uint32_t lastDiscoveryTime;
|
|
||||||
String routerLocation;
|
|
||||||
String controlURL;
|
|
||||||
char buffer[1024]; // Stack-based buffer for responses
|
|
||||||
|
|
||||||
public:
|
|
||||||
CUpnpClient() : publicPort(0), lastDiscoveryTime(0) {}
|
|
||||||
|
|
||||||
bool registerUPnP(uint32_t *pip, uint16_t *pport);
|
|
||||||
|
|
||||||
private:
|
|
||||||
String fetchUPnPDescription(const String &location);
|
|
||||||
String parseXML(const String &xml);
|
|
||||||
int sendSoapRequest(const char *request, char *response, size_t responseSize);
|
|
||||||
|
|
||||||
bool discoverUPnP();
|
|
||||||
bool requestPortMappingEntry();
|
|
||||||
bool requestPortForwarding();
|
|
||||||
bool requestExternalIP();
|
|
||||||
bool requestExternalPort();
|
|
||||||
};
|
|
||||||
|
|
@ -149,7 +149,7 @@ int ESPUpdateClass::update(WiFiClient& client,
|
||||||
{
|
{
|
||||||
HTTPClient http;
|
HTTPClient http;
|
||||||
if (!http.begin(client, host, port, uri)) {
|
if (!http.begin(client, host, port, uri)) {
|
||||||
Serial.println("OTA - httpClient begin failed");
|
DPRINTLN("OTA - httpClient begin failed");
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
19
zcd.cpp
19
zcd.cpp
|
|
@ -10,6 +10,7 @@
|
||||||
#include "hal/timer_types.h"
|
#include "hal/timer_types.h"
|
||||||
#include "driver/timer.h" // Needed for timer_isr_register
|
#include "driver/timer.h" // Needed for timer_isr_register
|
||||||
#include "rom/ets_sys.h"
|
#include "rom/ets_sys.h"
|
||||||
|
#include "driver/gpio.h"
|
||||||
|
|
||||||
|
|
||||||
timg_dev_t *tg0 = &TIMERG0;
|
timg_dev_t *tg0 = &TIMERG0;
|
||||||
|
|
@ -307,10 +308,22 @@ void setupZCD() {
|
||||||
//attachInterrupt(PIN_ZCD_LOAD, zcdLoadISR, CHANGE); // Attach zero-cross detection ISR
|
//attachInterrupt(PIN_ZCD_LOAD, zcdLoadISR, CHANGE); // Attach zero-cross detection ISR
|
||||||
|
|
||||||
// Attach Load ISR with higher priority
|
// Attach Load ISR with higher priority
|
||||||
esp_intr_alloc(ETS_GPIO_INTR_SOURCE, ESP_INTR_FLAG_LEVEL3, zcdLoadISR, NULL, NULL);
|
// Set the edges
|
||||||
|
gpio_config_t io_conf = {};
|
||||||
|
io_conf.intr_type = GPIO_INTR_ANYEDGE;
|
||||||
|
io_conf.pin_bit_mask = (1ULL << PIN_ZCD_AC) | (1ULL << PIN_ZCD_LOAD);
|
||||||
|
io_conf.mode = GPIO_MODE_INPUT;
|
||||||
|
gpio_config(&io_conf);
|
||||||
|
|
||||||
// Attach AC ISR with normal/lower priority
|
// 2. Set the GLOBAL Priority for all GPIOs
|
||||||
esp_intr_alloc(ETS_GPIO_INTR_SOURCE, ESP_INTR_FLAG_LEVEL1, zcdACISR, NULL, NULL);
|
// We use LEVEL3 so that BOTH ZCD pulses can preempt lower tasks.
|
||||||
|
// This replaces the attachInterrupt() default (Level 1).
|
||||||
|
gpio_install_isr_service(ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_LEVEL3);
|
||||||
|
|
||||||
|
// 3. Register the independent handlers
|
||||||
|
// The service handles the "Which pin was it?" logic automatically.
|
||||||
|
gpio_isr_handler_add((gpio_num_t)PIN_ZCD_LOAD, zcdLoadISR, NULL);
|
||||||
|
gpio_isr_handler_add((gpio_num_t)PIN_ZCD_AC, zcdACISR, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setACLoadStatus(uint32_t tNow) {
|
void setACLoadStatus(uint32_t tNow) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user