add UDP command processing and sender for dynamic testing

This commit is contained in:
2025-09-15 11:17:33 +02:00
parent 8591e7433d
commit 3384c89475
3 changed files with 187 additions and 0 deletions

View File

@@ -4,6 +4,14 @@
#include "esphome/core/helpers.h"
#include "helpers.h"
#ifdef USE_ESP32
#include <sstream>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <unistd.h>
#endif
namespace esphome {
namespace truma_inetbox {
@@ -49,6 +57,9 @@ void TrumaiNetBoxApp::update() {
ESP_LOGI(TAG, "Master mode: Discovery sent - expecting heater response on PID 3D");
}
// UDP command receiver
this->process_udp_commands();
// Master TX scheduler (throttle to ~20ms spacing)
if (this->master_mode_ && !this->master_tx_queue_.empty()) {
uint32_t now = micros();
@@ -503,4 +514,90 @@ void TrumaiNetBoxApp::trigger_discovery() {
ESP_LOGI(TAG, "=== DISCOVERY COMPLETED - HEATER SHOULD RESPOND ON PID 3D ===");
}
void TrumaiNetBoxApp::process_udp_commands() {
if (!this->master_mode_) return;
// Initialize UDP command receiver socket on port 5556 (one port higher than stream)
if (this->udp_cmd_sock_ < 0 && !this->udp_cmd_init_attempted_) {
this->udp_cmd_init_attempted_ = true;
this->udp_cmd_sock_ = socket(AF_INET, SOCK_DGRAM, 0);
if (this->udp_cmd_sock_ < 0) {
ESP_LOGW(TAG, "UDP command socket creation failed");
return;
}
// Set non-blocking
int flags = fcntl(this->udp_cmd_sock_, F_GETFL, 0);
fcntl(this->udp_cmd_sock_, F_SETFL, flags | O_NONBLOCK);
// Bind to port 5556
struct sockaddr_in addr;
memset(&addr, 0, sizeof(addr));
addr.sin_family = AF_INET;
addr.sin_addr.s_addr = INADDR_ANY;
addr.sin_port = htons(5556);
if (bind(this->udp_cmd_sock_, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
ESP_LOGW(TAG, "UDP command socket bind failed");
close(this->udp_cmd_sock_);
this->udp_cmd_sock_ = -1;
return;
}
ESP_LOGI(TAG, "UDP command receiver listening on port 5556");
ESP_LOGI(TAG, "Send commands like: 'CMD:B2 23 17 46 10 03 NAD:01'");
}
// Check for incoming commands
if (this->udp_cmd_sock_ >= 0) {
char buffer[256];
struct sockaddr_in sender_addr;
socklen_t addr_len = sizeof(sender_addr);
ssize_t len = recvfrom(this->udp_cmd_sock_, buffer, sizeof(buffer) - 1, 0,
(struct sockaddr*)&sender_addr, &addr_len);
if (len > 0) {
buffer[len] = '\0';
std::string cmd(buffer);
ESP_LOGI(TAG, "UDP Command received: %s", cmd.c_str());
// Parse command format: "CMD:B2 23 17 46 10 03 NAD:01"
size_t cmd_pos = cmd.find("CMD:");
size_t nad_pos = cmd.find("NAD:");
if (cmd_pos != std::string::npos && nad_pos != std::string::npos) {
std::string hex_data = cmd.substr(cmd_pos + 4, nad_pos - cmd_pos - 5);
std::string nad_str = cmd.substr(nad_pos + 4);
// Parse NAD
uint8_t nad = (uint8_t)strtoul(nad_str.c_str(), nullptr, 16);
// Parse hex bytes
std::vector<uint8_t> payload;
std::istringstream iss(hex_data);
std::string byte_str;
while (iss >> byte_str) {
if (byte_str.length() == 2) {
uint8_t byte_val = (uint8_t)strtoul(byte_str.c_str(), nullptr, 16);
payload.push_back(byte_val);
}
}
if (!payload.empty()) {
ESP_LOGI(TAG, "Sending diagnostic command to NAD 0x%02X with %zu bytes", nad, payload.size());
this->master_send_diag_single(nad, payload);
} else {
ESP_LOGW(TAG, "Failed to parse command payload");
}
} else {
ESP_LOGW(TAG, "Invalid command format. Use: CMD:B2 23 17 46 10 03 NAD:01");
}
}
}
}
} }

View File

@@ -32,6 +32,7 @@ class TrumaiNetBoxApp : public LinBusProtocol {
bool master_send_diag_single(uint8_t nad, const std::vector<uint8_t> &payload);
bool master_scan_b2(uint8_t nad, uint8_t ident_start, uint8_t ident_end );
void trigger_discovery();
void process_udp_commands();
TRUMA_DEVICE get_heater_device() const { return this->heater_device_; }
TRUMA_DEVICE get_aircon_device() const { return this->aircon_device_; }
@@ -96,6 +97,10 @@ class TrumaiNetBoxApp : public LinBusProtocol {
uint32_t last_master_send_us_ = 0;
bool master_discovery_started_ = false;
// UDP command receiver socket
int udp_cmd_sock_ = -1;
bool udp_cmd_init_attempted_ = false;
};
} // namespace truma_inetbox