Enhance DALI device type discovery and query handling

- Introduce DaliDeviceTypeDiscovery struct for managing device type queries.
- Implement discoverDeviceTypes method in DaliBase for improved device type detection.
- Update DaliComm to handle query responses more robustly, including handling multiple device replies.
- Add setDetectedDeviceTypes method in DaliDevice to manage detected device types and capabilities.
This commit is contained in:
Tony
2026-04-25 12:59:16 +08:00
parent bbcfcd11f6
commit 32e7329b3e
7 changed files with 225 additions and 14 deletions
+78 -12
View File
@@ -1,5 +1,7 @@
#include "dali_comm.hpp"
#include "dali_define.hpp"
#include <chrono>
#include <thread>
@@ -8,6 +10,61 @@
#include "freertos/task.h"
#endif
namespace {
constexpr uint32_t kQueryTimeoutMs = 100;
std::vector<uint8_t> readQueryResponse(const DaliComm::ReadCallback& read_cb, uint32_t timeout_ms) {
if (!read_cb) return {};
const auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout_ms);
std::vector<uint8_t> collected;
while (std::chrono::steady_clock::now() < deadline && collected.size() < 2) {
const auto remaining =
std::chrono::duration_cast<std::chrono::milliseconds>(deadline - std::chrono::steady_clock::now())
.count();
if (remaining <= 0) break;
const auto chunk = read_cb(2 - collected.size(), static_cast<uint32_t>(remaining));
if (chunk.empty()) break;
const size_t needed = 2 - collected.size();
const size_t take = std::min(needed, chunk.size());
collected.insert(collected.end(), chunk.begin(), chunk.begin() + static_cast<std::ptrdiff_t>(take));
// Non-prefixed one-byte replies are complete as soon as they arrive.
if (collected.size() == 1 && collected[0] != 0xFF && collected[0] != 0xFE && collected[0] != 0xFD) {
break;
}
}
return collected;
}
std::optional<uint8_t> parseQueryResponse(const std::vector<uint8_t>& resp, uint8_t addr) {
if (resp.empty()) return std::nullopt;
// Gateway type 1 returns: 0xFF <data> on success; 0xFE 0x00 on no response;
// 0xFD xx on invalid frame. COMPARE is special: collisions still mean a match.
if (resp[0] == 0xFE) {
return std::nullopt;
}
if (resp[0] == 0xFD) {
if (addr == DALI_CMD_SPECIAL_COMPARE) {
return static_cast<uint8_t>(resp.size() >= 2 ? resp[1] : 0xFF);
}
return std::nullopt;
}
if (resp.size() == 1) {
return resp[0];
}
if (resp[0] == 0xFF && resp.size() >= 2) {
return resp[1];
}
return resp.back();
}
} // namespace
DaliComm::DaliComm(SendCallback send_cb,
ReadCallback read_cb,
TransactCallback transact_cb,
@@ -51,12 +108,15 @@ int DaliComm::checkGatewayType(int gateway) const {
const std::vector<uint8_t> newProbe{0x28, 0x01, static_cast<uint8_t>(gateway), 0x11, 0x00,
0x00, 0x00, 0x00, 0x00, 0xFF};
prepareForQuery();
const auto usbResp = transact_(usbProbe.data(), usbProbe.size());
if (!usbResp.empty() && usbResp[0] > 0) return 1;
prepareForQuery();
const auto legacyResp = transact_(legacyProbe.data(), legacyProbe.size());
if (legacyResp.size() >= 2 && legacyResp[0] == gateway) return 2;
prepareForQuery();
const auto newResp = transact_(newProbe.data(), newProbe.size());
if (newResp.size() >= 2 && newResp[0] == gateway) return 3;
@@ -114,6 +174,16 @@ bool DaliComm::writeFrame(const std::vector<uint8_t>& frame) const {
return send_(frame.data(), frame.size());
}
void DaliComm::prepareForQuery() const {
if (!read_) return;
for (int i = 0; i < 8; ++i) {
const auto data = read_(64, 1);
if (data.empty()) {
break;
}
}
}
void DaliComm::sleepMs(uint32_t ms) const {
if (delay_) {
delay_(ms);
@@ -139,18 +209,14 @@ bool DaliComm::sendExtCmd(uint8_t addr, uint8_t cmd) const {
}
std::optional<uint8_t> DaliComm::queryCmd(uint8_t addr, uint8_t cmd) const {
if (!transact_) return std::nullopt;
const std::vector<uint8_t> frame{0x12, addr, cmd};
const auto resp = transact_(frame.data(), frame.size());
if (resp.empty()) return std::nullopt;
if (send_ && read_) {
prepareForQuery();
if (!writeFrame(frame)) return std::nullopt;
return parseQueryResponse(readQueryResponse(read_, kQueryTimeoutMs), addr);
}
// Gateway type 1 returns: 0xFF <data> on success; 0xFE no response; 0xFD invalid frame.
if (resp.size() == 1) {
if (resp[0] == 0xFE || resp[0] == 0xFD) return std::nullopt;
return resp[0];
}
if (resp[0] == 0xFF && resp.size() >= 2) {
return resp[1];
}
return resp.back();
prepareForQuery();
if (!transact_) return std::nullopt;
return parseQueryResponse(transact_(frame.data(), frame.size()), addr);
}