Merge remote-tracking branch 'upstream/master'

This commit is contained in:
VietDzung 2025-03-06 19:29:30 +07:00
commit 6eb2048277
15 changed files with 125 additions and 78 deletions

2
.gitignore vendored
View File

@ -23,7 +23,7 @@ bld/
[Ll]og/ [Ll]og/
out out
flash.bin flash.bin
build build*
dist dist
*.egg-info *.egg-info
doxyoutput doxyoutput

View File

@ -1,3 +1,5 @@
#pragma once
#include "knx/platform.h" #include "knx/platform.h"
#include "Arduino.h" #include "Arduino.h"

View File

@ -8,6 +8,15 @@
#ifndef KNX_SERIAL #ifndef KNX_SERIAL
#define KNX_SERIAL Serial1 #define KNX_SERIAL Serial1
#pragma warn "KNX_SERIAL not defined, using Serial1"
#endif
#ifdef KNX_IP_LAN
#include "ETH.h"
#define KNX_NETIF ETH
#else // KNX_IP_WIFI
#include <WiFi.h>
#define KNX_NETIF WiFi
#endif #endif
Esp32Platform::Esp32Platform() Esp32Platform::Esp32Platform()
@ -44,22 +53,22 @@ void Esp32Platform::setupUart()
uint32_t Esp32Platform::currentIpAddress() uint32_t Esp32Platform::currentIpAddress()
{ {
return WiFi.localIP(); return KNX_NETIF.localIP();
} }
uint32_t Esp32Platform::currentSubnetMask() uint32_t Esp32Platform::currentSubnetMask()
{ {
return WiFi.subnetMask(); return KNX_NETIF.subnetMask();
} }
uint32_t Esp32Platform::currentDefaultGateway() uint32_t Esp32Platform::currentDefaultGateway()
{ {
return WiFi.gatewayIP(); return KNX_NETIF.gatewayIP();
} }
void Esp32Platform::macAddress(uint8_t* addr) void Esp32Platform::macAddress(uint8_t* addr)
{ {
esp_wifi_get_mac(WIFI_IF_STA, addr); KNX_NETIF.macAddress(addr);
} }
uint32_t Esp32Platform::uniqueSerialNumber() uint32_t Esp32Platform::uniqueSerialNumber()
@ -82,7 +91,7 @@ void Esp32Platform::setupMultiCast(uint32_t addr, uint16_t port)
IPAddress mcastaddr(htonl(addr)); IPAddress mcastaddr(htonl(addr));
KNX_DEBUG_SERIAL.printf("setup multicast addr: %s port: %d ip: %s\n", mcastaddr.toString().c_str(), port, KNX_DEBUG_SERIAL.printf("setup multicast addr: %s port: %d ip: %s\n", mcastaddr.toString().c_str(), port,
WiFi.localIP().toString().c_str()); KNX_NETIF.localIP().toString().c_str());
uint8_t result = _udp.beginMulticast(mcastaddr, port); uint8_t result = _udp.beginMulticast(mcastaddr, port);
KNX_DEBUG_SERIAL.printf("result %d\n", result); KNX_DEBUG_SERIAL.printf("result %d\n", result);
} }

View File

@ -1,9 +1,10 @@
#ifdef ARDUINO_ARCH_ESP32 #ifdef ARDUINO_ARCH_ESP32
#include "arduino_platform.h" #include "arduino_platform.h"
#include <WiFi.h>
#include <WiFiUdp.h>
#include <WiFiUdp.h>
class Esp32Platform : public ArduinoPlatform class Esp32Platform : public ArduinoPlatform
{ {
public: public:

View File

@ -188,19 +188,25 @@ TPAckType Bau091A::isAckRequired(uint16_t address, bool isGrpAddr)
{ {
// ACK for broadcasts // ACK for broadcasts
if (address == 0) if (address == 0)
{
ack = TPAckType::AckReqAck; ack = TPAckType::AckReqAck;
}
if (lcconfig & LCCONFIG::GROUP_IACK_ROUT)
// is group address in filter table? ACK if yes, No if not
if (_netLayer.isRoutedGroupAddress(address, 1))
ack = TPAckType::AckReqAck;
else
ack = TPAckType::AckReqNone;
else else
// all are ACKED {
ack = TPAckType::AckReqAck; if(lcconfig & LCCONFIG::GROUP_IACK_ROUT)
{
// is group address in filter table? ACK if yes, No if not
if(_netLayer.isRoutedGroupAddress(address, 1))
ack = TPAckType::AckReqAck;
else
ack = TPAckType::AckReqNone;
}
else
{
// all are ACKED
ack = TPAckType::AckReqAck;
}
}
#ifdef KNX_TUNNELING #ifdef KNX_TUNNELING
if (_dlLayerPrimary.isSentToTunnel(address, isGrpAddr)) if (_dlLayerPrimary.isSentToTunnel(address, isGrpAddr))
@ -226,7 +232,6 @@ TPAckType Bau091A::isAckRequired(uint16_t address, bool isGrpAddr)
#endif #endif
} }
return ack; return ack;
} }

View File

@ -6,15 +6,15 @@
class DeviceObject: public InterfaceObject class DeviceObject: public InterfaceObject
{ {
public: public:
// increase this version anytime DeviceObject-API changes // increase this version anytime DeviceObject-API changes
// the following value represents the serialized representation of DeviceObject. // the following value represents the serialized representation of DeviceObject.
const uint16_t apiVersion = 1; const uint16_t apiVersion = 2;
DeviceObject(); DeviceObject();
uint8_t* save(uint8_t* buffer) override; uint8_t* save(uint8_t* buffer) override;
const uint8_t* restore(const uint8_t* buffer) override; const uint8_t* restore(const uint8_t* buffer) override;
uint16_t saveSize() override; uint16_t saveSize() override;
uint16_t individualAddress(); uint16_t individualAddress();
void individualAddress(uint16_t value); void individualAddress(uint16_t value);

View File

@ -242,10 +242,15 @@ GroupObjectUpdatedHandler GroupObject::callback()
} }
#endif #endif
void GroupObject::value(const KNXValue& value, const Dpt& type) bool GroupObject::value(const KNXValue& value, const Dpt& type)
{ {
valueNoSend(value, type); if (valueNoSend(value, type))
objectWritten(); {
// write on successful conversion/setting value only
objectWritten();
return true;
}
return false;
} }
@ -280,9 +285,9 @@ bool GroupObject::tryValue(KNXValue& value)
} }
void GroupObject::value(const KNXValue& value) bool GroupObject::value(const KNXValue& value)
{ {
this->value(value, _datapointType); return this->value(value, _datapointType);
} }
@ -292,18 +297,21 @@ KNXValue GroupObject::value()
} }
void GroupObject::valueNoSend(const KNXValue& value) bool GroupObject::valueNoSend(const KNXValue& value)
{ {
valueNoSend(value, _datapointType); return valueNoSend(value, _datapointType);
} }
#endif #endif
void GroupObject::valueNoSend(const KNXValue& value, const Dpt& type) bool GroupObject::valueNoSend(const KNXValue& value, const Dpt& type)
{ {
if (_uninitialized) const bool encodingDone = KNX_Encode_Value(value, _data, _dataLength, type);
// initialize on succesful conversion only
if (encodingDone && _uninitialized)
commFlag(Ok); commFlag(Ok);
KNX_Encode_Value(value, _data, _dataLength, type); return encodingDone;
} }
bool GroupObject::valueNoSendCompare(const KNXValue& value, const Dpt& type) bool GroupObject::valueNoSendCompare(const KNXValue& value, const Dpt& type)
@ -311,15 +319,20 @@ bool GroupObject::valueNoSendCompare(const KNXValue& value, const Dpt& type)
if (_uninitialized) if (_uninitialized)
{ {
// always set first value // always set first value
this->valueNoSend(value, type); return valueNoSend(value, type);
return true;
} }
else else
{ {
// convert new value to given dtp // convert new value to given DPT
uint8_t newData[_dataLength]; uint8_t newData[_dataLength];
memset(newData, 0, _dataLength); memset(newData, 0, _dataLength);
KNX_Encode_Value(value, newData, _dataLength, type); const bool encodingDone = KNX_Encode_Value(value, newData, _dataLength, type);
if (!encodingDone)
{
// value conversion to DPT failed
// do NOT update the value of the KO!
return false;
}
// check for change in converted value / update value on change only // check for change in converted value / update value on change only
const bool dataChanged = memcmp(_data, newData, _dataLength); const bool dataChanged = memcmp(_data, newData, _dataLength);

View File

@ -162,8 +162,10 @@ class GroupObject
* @param type the datapoint type used for the conversion. * @param type the datapoint type used for the conversion.
* *
* The parameters must fit the group object. Otherwise it will stay unchanged. * The parameters must fit the group object. Otherwise it will stay unchanged.
*
* @returns true if the value was converted successfully to the datapoint type and the group object was updated.
*/ */
void value(const KNXValue& value, const Dpt& type); bool value(const KNXValue& value, const Dpt& type);
/** /**
* Check if the value (after conversion to dpt) will differ from current value of the group object and changes the state of the group object to ::WriteRequest if different. * Check if the value (after conversion to dpt) will differ from current value of the group object and changes the state of the group object to ::WriteRequest if different.
@ -173,18 +175,20 @@ class GroupObject
* *
* The parameters must fit the group object. Otherwise it will stay unchanged. * The parameters must fit the group object. Otherwise it will stay unchanged.
* *
* @returns true if the value of the group object has changed * @returns true if the value of the group object has changed, false if conversion results in same value as stored in group object or failed.
*/ */
bool valueCompare(const KNXValue& value, const Dpt& type); bool valueCompare(const KNXValue& value, const Dpt& type);
/** /**
* set the current value of the group object. * set the current value of the group objectand show success.
* @param value the value the group object is set to * @param value the value the group object is set to
* @param type the datapoint type used for the conversion. * @param type the datapoint type used for the conversion.
* *
* The parameters must fit the group object. Otherwise it will stay unchanged. * The parameters must fit the group object. Otherwise it will stay unchanged.
*
* @returns true if value was converted successfully to the datapoint type and the group object was updated.
*/ */
void valueNoSend(const KNXValue& value, const Dpt& type); bool valueNoSend(const KNXValue& value, const Dpt& type);
/** /**
* Check if the value (after conversion to dpt) will differ from current value of the group object and update if necessary. * Check if the value (after conversion to dpt) will differ from current value of the group object and update if necessary.
@ -194,7 +198,7 @@ class GroupObject
* *
* The parameters must fit the group object. Otherwise it will stay unchanged. * The parameters must fit the group object. Otherwise it will stay unchanged.
* *
* @returns true if the value of the group object has changed * @returns true if the value of the group object has changed, false if conversion results in same value as stored in group object or failed.
*/ */
bool valueNoSendCompare(const KNXValue& value, const Dpt& type); bool valueNoSendCompare(const KNXValue& value, const Dpt& type);
@ -220,15 +224,19 @@ class GroupObject
* @param value the value the group object is set to * @param value the value the group object is set to
* *
* The parameters must fit the group object and dhe datapoint type must be set with dataPointType(). Otherwise it will stay unchanged. * The parameters must fit the group object and dhe datapoint type must be set with dataPointType(). Otherwise it will stay unchanged.
*
* @returns true if the value was converted successfully to the datapoint type and the group object was updated.
*/ */
void value(const KNXValue& value); bool value(const KNXValue& value);
/** /**
* set the current value of the group object. * set the current value of the group object.
* @param value the value the group object is set to * @param value the value the group object is set to
* *
* The parameters must fit the group object and dhe datapoint type must be set with dataPointType(). Otherwise it will stay unchanged. * The parameters must fit the group object and the datapoint type must be set with dataPointType(). Otherwise it will stay unchanged.
*
* @returns true if the value was converted successfully to the datapoint type and the group object was updated.
*/ */
void valueNoSend(const KNXValue& value); bool valueNoSend(const KNXValue& value);
/** /**
* set the current value of the group object. * set the current value of the group object.
* @param value the value the group object is set to * @param value the value the group object is set to

View File

@ -504,9 +504,8 @@ void IpDataLinkLayer::loopHandleSearchRequestExtended(uint8_t* buffer, uint16_t
if (searchRequest.srpRequestDIBs) if (searchRequest.srpRequestDIBs)
{ {
println("srpRequestDIBs"); //println("srpRequestDIBs");
if(searchRequest.requestedDIB(IP_CONFIG))
if (searchRequest.requestedDIB(IP_CONFIG))
dibLength += LEN_IP_CONFIG_DIB; //16 dibLength += LEN_IP_CONFIG_DIB; //16
if (searchRequest.requestedDIB(IP_CUR_CONFIG)) if (searchRequest.requestedDIB(IP_CUR_CONFIG))
@ -556,9 +555,9 @@ void IpDataLinkLayer::loopHandleSearchRequestExtended(uint8_t* buffer, uint16_t
searchResponse.setTunnelingInfo(_ipParameters, _deviceObject, tunnels); searchResponse.setTunnelingInfo(_ipParameters, _deviceObject, tunnels);
} }
if (searchResponse.totalLength() > 150) if(searchResponse.totalLength() > 500)
{ {
println("skipped response cause length is not plausible"); printf("skipped response length > 500. Length: %d bytes\n", searchResponse.totalLength());
return; return;
} }
@ -715,8 +714,7 @@ void IpDataLinkLayer::loopHandleConnectRequest(uint8_t* buffer, uint16_t length,
tunnelResOptions[i] = (*(tunCtrlBytes + i) & 0x60) >> 5; tunnelResOptions[i] = (*(tunCtrlBytes + i) & 0x60) >> 5;
} }
if(resTunActive && tunnelResActive[i]) // tunnel reserve feature active for this tunnel
if (tunnelResActive[i]) // tunnel reserve feature active for this tunnel
{ {
#ifdef KNX_LOG_TUNNELING #ifdef KNX_LOG_TUNNELING
print("tunnel reserve feature active for this tunnel: "); print("tunnel reserve feature active for this tunnel: ");
@ -834,8 +832,9 @@ void IpDataLinkLayer::loopHandleConnectRequest(uint8_t* buffer, uint16_t length,
tun = nullptr; tun = nullptr;
break; break;
} }
if(tun)
tun->IndividualAddress = tunPa;
tun->IndividualAddress = tunPa;
} }
if (tun == nullptr) if (tun == nullptr)
@ -869,8 +868,8 @@ void IpDataLinkLayer::loopHandleConnectRequest(uint8_t* buffer, uint16_t length,
_lastChannelId = 0; _lastChannelId = 0;
tun->IpAddress = srcIP; tun->IpAddress = srcIP;
tun->PortData = srcPort; tun->PortData = connRequest.hpaiData().ipPortNumber()?connRequest.hpaiData().ipPortNumber():srcPort;
tun->PortCtrl = connRequest.hpaiCtrl().ipPortNumber() ? connRequest.hpaiCtrl().ipPortNumber() : srcPort; tun->PortCtrl = connRequest.hpaiCtrl().ipPortNumber()?connRequest.hpaiCtrl().ipPortNumber():srcPort;
print("New Tunnel-Connection["); print("New Tunnel-Connection[");
print(tunIdx); print(tunIdx);

View File

@ -52,8 +52,8 @@ class IpDataLinkLayer : public DataLinkLayer
IpParameterObject& _ipParameters; IpParameterObject& _ipParameters;
DataLinkLayerCallbacks* _dllcb; DataLinkLayerCallbacks* _dllcb;
#ifdef KNX_TUNNELING #ifdef KNX_TUNNELING
KnxIpTunnelConnection tunnels[KNX_TUNNELING]; KnxIpTunnelConnection tunnels[KNX_TUNNELING];
uint8_t _lastChannelId = 1; uint8_t _lastChannelId = 0;
#endif #endif
}; };
#endif #endif

View File

@ -17,7 +17,8 @@ KnxIpConnectResponse::KnxIpConnectResponse(IpParameterObject& parameters, uint16
_crd.length((type == 4) ? 4 : 2); //TunnelConnectionResponse length = 4; ConfigConnectionResponse length = 2; _crd.length((type == 4) ? 4 : 2); //TunnelConnectionResponse length = 4; ConfigConnectionResponse length = 2;
_crd.type(type); _crd.type(type);
_crd.address(address); if(type == 4) // only fill address when it is a TunnelConnectionResponse
_crd.address(address);
} }
KnxIpConnectResponse::KnxIpConnectResponse(uint8_t channel, uint8_t errorCode) KnxIpConnectResponse::KnxIpConnectResponse(uint8_t channel, uint8_t errorCode)

View File

@ -34,7 +34,7 @@ KnxIpSearchResponseExtended::KnxIpSearchResponseExtended(IpParameterObject& para
void KnxIpSearchResponseExtended::setDeviceInfo(IpParameterObject& parameters, DeviceObject& deviceObject) void KnxIpSearchResponseExtended::setDeviceInfo(IpParameterObject& parameters, DeviceObject& deviceObject)
{ {
println("setDeviceInfo"); //setDeviceInfo");
KnxIpDeviceInformationDIB _deviceInfo(_data + currentPos); KnxIpDeviceInformationDIB _deviceInfo(_data + currentPos);
_deviceInfo.length(LEN_DEVICE_INFORMATION_DIB); _deviceInfo.length(LEN_DEVICE_INFORMATION_DIB);
_deviceInfo.code(DEVICE_INFO); _deviceInfo.code(DEVICE_INFO);
@ -65,7 +65,7 @@ void KnxIpSearchResponseExtended::setDeviceInfo(IpParameterObject& parameters, D
void KnxIpSearchResponseExtended::setSupportedServices() void KnxIpSearchResponseExtended::setSupportedServices()
{ {
println("setSupportedServices"); //println("setSupportedServices");
KnxIpSupportedServiceDIB _supportedServices(_data + currentPos); KnxIpSupportedServiceDIB _supportedServices(_data + currentPos);
_supportedServices.length(LEN_SERVICE_DIB); _supportedServices.length(LEN_SERVICE_DIB);
_supportedServices.code(SUPP_SVC_FAMILIES); _supportedServices.code(SUPP_SVC_FAMILIES);
@ -82,7 +82,7 @@ void KnxIpSearchResponseExtended::setSupportedServices()
void KnxIpSearchResponseExtended::setIpConfig(IpParameterObject& parameters) void KnxIpSearchResponseExtended::setIpConfig(IpParameterObject& parameters)
{ {
println("setIpConfig"); //println("setIpConfig");
KnxIpConfigDIB _ipConfig(_data + currentPos); KnxIpConfigDIB _ipConfig(_data + currentPos);
_ipConfig.length(LEN_IP_CONFIG_DIB); _ipConfig.length(LEN_IP_CONFIG_DIB);
_ipConfig.code(IP_CONFIG); _ipConfig.code(IP_CONFIG);
@ -97,14 +97,14 @@ void KnxIpSearchResponseExtended::setIpConfig(IpParameterObject& parameters)
void KnxIpSearchResponseExtended::setIpCurrentConfig(IpParameterObject& parameters) void KnxIpSearchResponseExtended::setIpCurrentConfig(IpParameterObject& parameters)
{ {
println("setIpCurrentConfig"); //println("setIpCurrentConfig");
KnxIpConfigDIB _ipCurConfig(_data + currentPos, true); KnxIpConfigDIB _ipCurConfig(_data + currentPos, true);
_ipCurConfig.length(LEN_IP_CURRENT_CONFIG_DIB); _ipCurConfig.length(LEN_IP_CURRENT_CONFIG_DIB);
_ipCurConfig.code(IP_CUR_CONFIG); _ipCurConfig.code(IP_CUR_CONFIG);
_ipCurConfig.address(parameters.propertyValue<uint32_t>(PID_CURRENT_IP_ADDRESS)); _ipCurConfig.address(parameters.propertyValue<uint32_t>(PID_CURRENT_IP_ADDRESS));
_ipCurConfig.subnet(parameters.propertyValue<uint32_t>(PID_CURRENT_SUBNET_MASK)); _ipCurConfig.subnet(parameters.propertyValue<uint32_t>(PID_CURRENT_SUBNET_MASK));
_ipCurConfig.gateway(parameters.propertyValue<uint32_t>(PID_CURRENT_DEFAULT_GATEWAY)); _ipCurConfig.gateway(parameters.propertyValue<uint32_t>(PID_CURRENT_DEFAULT_GATEWAY));
_ipCurConfig.dhcp(parameters.propertyValue<uint32_t>(PID_DHCP_BOOTP_SERVER)); _ipCurConfig.dhcp(0);
_ipCurConfig.info1(parameters.propertyValue<uint8_t>(PID_CURRENT_IP_ASSIGNMENT_METHOD)); _ipCurConfig.info1(parameters.propertyValue<uint8_t>(PID_CURRENT_IP_ASSIGNMENT_METHOD));
_ipCurConfig.info2(0x00); //Reserved _ipCurConfig.info2(0x00); //Reserved
@ -113,7 +113,7 @@ void KnxIpSearchResponseExtended::setIpCurrentConfig(IpParameterObject& paramete
void KnxIpSearchResponseExtended::setKnxAddresses(IpParameterObject& parameters, DeviceObject& deviceObject) void KnxIpSearchResponseExtended::setKnxAddresses(IpParameterObject& parameters, DeviceObject& deviceObject)
{ {
println("setKnxAddresses"); //println("setKnxAddresses");
KnxIpKnxAddressesDIB _knxAddresses(_data + currentPos); KnxIpKnxAddressesDIB _knxAddresses(_data + currentPos);
_knxAddresses.length(4); //minimum _knxAddresses.length(4); //minimum
_knxAddresses.code(KNX_ADDRESSES); _knxAddresses.code(KNX_ADDRESSES);
@ -136,7 +136,7 @@ void KnxIpSearchResponseExtended::setKnxAddresses(IpParameterObject& parameters,
void KnxIpSearchResponseExtended::setTunnelingInfo(IpParameterObject& parameters, DeviceObject& deviceObject, KnxIpTunnelConnection tunnels[]) void KnxIpSearchResponseExtended::setTunnelingInfo(IpParameterObject& parameters, DeviceObject& deviceObject, KnxIpTunnelConnection tunnels[])
{ {
println("setTunnelingInfo"); //println("setTunnelingInfo");
KnxIpTunnelingInfoDIB _tunnelInfo(_data + currentPos); KnxIpTunnelingInfoDIB _tunnelInfo(_data + currentPos);
_tunnelInfo.length(4); //minlength _tunnelInfo.length(4); //minlength
_tunnelInfo.code(TUNNELING_INFO); _tunnelInfo.code(TUNNELING_INFO);
@ -202,7 +202,7 @@ void KnxIpSearchResponseExtended::setTunnelingInfo(IpParameterObject& parameters
void KnxIpSearchResponseExtended::setExtendedDeviceInfo() void KnxIpSearchResponseExtended::setExtendedDeviceInfo()
{ {
println("setExtendedDeviceInfo"); //println("setExtendedDeviceInfo");
KnxIpExtendedDeviceInformationDIB _extended(_data + currentPos); KnxIpExtendedDeviceInformationDIB _extended(_data + currentPos);
_extended.length(LEN_EXTENDED_DEVICE_INFORMATION_DIB); _extended.length(LEN_EXTENDED_DEVICE_INFORMATION_DIB);
_extended.code(EXTENDED_DEVICE_INFO); _extended.code(EXTENDED_DEVICE_INFO);

View File

@ -7,6 +7,11 @@ KnxIpTunnelConnection::KnxIpTunnelConnection()
void KnxIpTunnelConnection::Reset() void KnxIpTunnelConnection::Reset()
{ {
print("Close Tunnel-Connection[");
print("?");
print("], Channel: 0x");
println(ChannelId, 16);
ChannelId = 0; ChannelId = 0;
IpAddress = 0; IpAddress = 0;
PortData = 0; PortData = 0;

View File

@ -657,6 +657,15 @@ void TpUartDataLinkLayer::requestConfig()
if (markerMode()) if (markerMode())
_platform.writeUart(U_CONFIGURE_REQ | U_CONFIGURE_MARKER_REQ); _platform.writeUart(U_CONFIGURE_REQ | U_CONFIGURE_MARKER_REQ);
#endif
// Set Address for AutoACK Unicast
const uint16_t address = _deviceObject.individualAddress();
_platform.writeUart(U_SET_ADDRESS_REQ);
_platform.writeUart((address >> 8) & 0xFF);
_platform.writeUart(address & 0xFF);
#ifdef NCN5120
_platform.writeUart(0xFF); // Dummy Byte needed by NCN only
#endif #endif
// Deviating Config // Deviating Config

View File

@ -470,12 +470,7 @@ uint32_t RP2040ArduinoPlatform::currentDefaultGateway()
} }
void RP2040ArduinoPlatform::macAddress(uint8_t* addr) void RP2040ArduinoPlatform::macAddress(uint8_t* addr)
{ {
#if defined(KNX_IP_LAN) KNX_NETIF.macAddress(addr);
addr = KNX_NETIF.getNetIf()->hwaddr;
#else
uint8_t macaddr[6] = {0, 0, 0, 0, 0, 0};
addr = KNX_NETIF.macAddress(macaddr);
#endif
} }
// multicast // multicast