Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ See the examples for basic usage options
- the Device Management Connection is now handled independently from Tunnel Connections and do not consume a Tunnel PA anymore
new compiler option KNX_TUNNELING_DEVMGMT (default = 1) to set the number of availible Device Management Connections
- new Compiler Option KNX_ROUTING_BC_DC: Unicast packets from the device itself is sent to both interfaces (IP and TP in case of 0x091A)
- change default PID_MAX_APDU_LENGTH_ROUTER from 220 to 254
- fix broken ConfigReq Responses
- fix programming application when FlashTablesInvalid for 0x091A

### v2.3.1 - 2026-03-04

Expand Down
2 changes: 1 addition & 1 deletion src/knx/bau091A.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ Bau091A::Bau091A(Platform& platform)
{
// Before accessing anything of the router object they have to be initialized according to the used medium
// Coupler model 1.x
_routerObj.initialize1x(DptMedium::KNX_IP, 220);
_routerObj.initialize1x(DptMedium::KNX_IP, 254);

// Mask 091A uses older coupler model 1.x which only uses one router object
_netLayer.rtObj(_routerObj);
Expand Down
24 changes: 12 additions & 12 deletions src/knx/cemi_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ void CemiServer::dataIndicationToTunnel(CemiFrame& frame)
#endif
}

void CemiServer::frameReceived(CemiFrame& frame)
void CemiServer::frameReceived(CemiFrame& frame, uint8_t channelId)
{
switch(frame.messageCode())
{
Expand All @@ -137,13 +137,13 @@ void CemiServer::frameReceived(CemiFrame& frame)

case M_PropRead_req:
{
handleMPropRead(frame);
handleMPropRead(frame, channelId);
break;
}

case M_PropWrite_req:
{
handleMPropWrite(frame);
handleMPropWrite(frame, channelId);
break;
}

Expand All @@ -161,7 +161,7 @@ void CemiServer::frameReceived(CemiFrame& frame)

case M_Reset_req:
{
handleMReset(frame);
handleMReset(frame, channelId);
break;
}

Expand Down Expand Up @@ -234,7 +234,7 @@ void CemiServer::handleLData(CemiFrame& frame)
_dataLinkLayer->dataRequestFromTunnel(frame);
}

void CemiServer::handleMPropRead(CemiFrame& frame)
void CemiServer::handleMPropRead(CemiFrame& frame, uint8_t channelId)
{
#ifdef KNX_LOG_TUNNELING
print("M_PropRead_req: ");
Expand Down Expand Up @@ -299,7 +299,7 @@ void CemiServer::handleMPropRead(CemiFrame& frame)
#ifdef USE_USB
_usbTunnelInterface.sendCemiFrame(responseFrame);
#elif defined(KNX_TUNNELING)
_ipTunnelServer.dataRequestToTunnel(responseFrame);
_ipTunnelServer.dataRequestToChannelId(responseFrame, channelId);
#endif
delete[] data;
}
Expand All @@ -319,12 +319,12 @@ void CemiServer::handleMPropRead(CemiFrame& frame)
#ifdef USE_USB
_usbTunnelInterface.sendCemiFrame(responseFrame);
#elif defined(KNX_TUNNELING)
_ipTunnelServer.dataRequestToTunnel(responseFrame);
_ipTunnelServer.dataRequestToChannelId(responseFrame, channelId);
#endif
}
}

void CemiServer::handleMPropWrite(CemiFrame& frame)
void CemiServer::handleMPropWrite(CemiFrame& frame, uint8_t channelId)
{
print("M_PropWrite_req: ");

Expand Down Expand Up @@ -389,7 +389,7 @@ void CemiServer::handleMPropWrite(CemiFrame& frame)
#ifdef USE_USB
_usbTunnelInterface.sendCemiFrame(responseFrame);
#elif defined(KNX_TUNNELING)
_ipTunnelServer.dataRequestToTunnel(responseFrame);
_ipTunnelServer.dataRequestToChannelId(responseFrame, channelId);
#endif
}
else
Expand All @@ -408,12 +408,12 @@ void CemiServer::handleMPropWrite(CemiFrame& frame)
#ifdef USE_USB
_usbTunnelInterface.sendCemiFrame(responseFrame);
#elif defined(KNX_TUNNELING)
_ipTunnelServer.dataRequestToTunnel(responseFrame);
_ipTunnelServer.dataRequestToChannelId(responseFrame, channelId);
#endif
}
}

void CemiServer::handleMReset(CemiFrame& frame)
void CemiServer::handleMReset(CemiFrame& frame, uint8_t channelId)
{
println("M_Reset_req: sending M_Reset_ind");
// A real device reset does not work for USB or KNXNET/IP.
Expand All @@ -427,7 +427,7 @@ void CemiServer::handleMReset(CemiFrame& frame)
#ifdef USE_USB
_usbTunnelInterface.sendCemiFrame(responseFrame);
#elif defined(KNX_TUNNELING)
_ipTunnelServer.dataRequestToTunnel(responseFrame);
_ipTunnelServer.dataRequestToChannelId(responseFrame, channelId);
#endif
}

Expand Down
8 changes: 4 additions & 4 deletions src/knx/cemi_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class CemiServer
void dataConfirmationToTunnel(CemiFrame& frame);

// From tunnel interface
void frameReceived(CemiFrame& frame);
void frameReceived(CemiFrame& frame, uint8_t channelId);

uint16_t clientAddress() const;
void clientAddress(uint16_t value);
Expand All @@ -56,9 +56,9 @@ class CemiServer
uint8_t _frameNumber = 0;

void handleLData(CemiFrame& frame);
void handleMPropRead(CemiFrame& frame);
void handleMPropWrite(CemiFrame& frame);
void handleMReset(CemiFrame& frame);
void handleMPropRead(CemiFrame& frame, uint8_t channelId);
void handleMPropWrite(CemiFrame& frame, uint8_t channelId);
void handleMReset(CemiFrame& frame, uint8_t channelId);

DataLinkLayer* _dataLinkLayer = nullptr;
#ifdef KNX_TUNNELING
Expand Down
40 changes: 38 additions & 2 deletions src/knx/ip_tunnel_server.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,42 @@ void IpTunnelServer::loop()
}
}

void IpTunnelServer::dataRequestToChannelId(CemiFrame& frame, uint8_t channelId)
{
KnxIpTunnelConnection* tun = nullptr;
for (int i = 0; i < KNX_TUNNELING + KNX_TUNNELING_DEVMGMT; i++)
{
#ifdef KNX_LOG_TUNNELING
print("Tunnel ChannelId: ");
#endif
if(tunnels[i].IsConfig)
{
#ifdef KNX_LOG_TUNNELING
print("Config ");
#endif
}
#ifdef KNX_LOG_TUNNELING
println(tunnels[i].ChannelId, 16);
#endif
if (tunnels[i].ChannelId == channelId)
{
tun = &tunnels[i];
break;
}
}

if (tun == nullptr)
{
#ifdef KNX_LOG_TUNNELING
print("Found no Tunnel for ChannelId: ");
println(channelId, 16);
#endif
return;
}

sendFrameToTunnel(tun, frame);
}

void IpTunnelServer::dataRequestToTunnel(CemiFrame& frame)
{
if (frame.addressType() == AddressType::GroupAddress)
Expand Down Expand Up @@ -750,7 +786,7 @@ void IpTunnelServer::HandleDeviceConfigurationRequest(uint8_t* buffer, uint16_t
_platform.sendBytesUniCast(tun->IpAddress, tun->PortData, tunnAck.data(), tunnAck.totalLength());

tun->lastHeartbeat = millis();
_cemiServer.frameReceived(confReq.frame());
_cemiServer.frameReceived(confReq.frame(), tun->ChannelId);
}

void IpTunnelServer::HandleTunnelingRequest(uint8_t* buffer, uint16_t length)
Expand Down Expand Up @@ -819,7 +855,7 @@ void IpTunnelServer::HandleTunnelingRequest(uint8_t* buffer, uint16_t length)
if (tunnReq.frame().sourceAddress() == 0)
tunnReq.frame().sourceAddress(tun->IndividualAddress);

_cemiServer.frameReceived(tunnReq.frame());
_cemiServer.frameReceived(tunnReq.frame(), tun->ChannelId);
}

#endif
1 change: 1 addition & 0 deletions src/knx/ip_tunnel_server.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class IpTunnelServer
IpTunnelServer(DeviceObject& devObj, IpParameterObject& ipParam, Platform& platform, CemiServer& cemiServer);

void loop();
void dataRequestToChannelId(CemiFrame& frame, uint8_t channelId);
void dataRequestToTunnel(CemiFrame& frame);
void dataConfirmationToTunnel(CemiFrame& frame);
void dataIndicationToTunnel(CemiFrame& frame);
Expand Down
37 changes: 28 additions & 9 deletions src/knx/memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -91,37 +91,56 @@ void Memory::readMemory()
}

println("restoring data from flash...");
print("saverestores ");
print("Restore saveRestores: ");
println(_saveCount);
for (int i = 0; i < _saveCount; i++)
{
println(flashStart - buffer);
println(".");
buffer = _saveRestores[i]->restore(buffer);
}
println("restored saveRestores");
println("Restored saveRestores");

#if MASK_VERSION == 0x091A
if(versionCheck == FlashTablesInvalid)
{
println("TableObjects are referring to an older firmware version and are restored, unloaded and filled with 0xff");
}
#else
if (versionCheck == FlashTablesInvalid)
{
println("TableObjects are referring to an older firmware version and are not loaded");
println("TableObjects are referring to an older firmware version and are not restored");
return;
}
print("tableObjs ");
#endif
print("Restore TableObjs: ");
println(_tableObjCount);
for (int i = 0; i < _tableObjCount; i++)
{
println(flashStart - buffer);
println(".");
buffer = _tableObjects[i]->restore(buffer);
uint16_t memorySize = 0;
buffer = popWord(memorySize, buffer);
print("Size: ");
println(memorySize);
if (memorySize == 0)
continue;

// this works because TableObject saves a relative addr and restores it itself
addNewUsedBlock(_tableObjects[i]->_data, memorySize);

#if MASK_VERSION == 0x091A
// load the tables but delete the data
if(versionCheck == FlashTablesInvalid)
{
println("unload and fill with 0xff");
_tableObjects[i]->loadState(LS_UNLOADED);
uint32_t start = toRelative(_tableObjects[i]->_data);
uint8_t fillByte = 0xff;
uint32_t end = start + _tableObjects[i]->_size;
for(int i = start;i < end;i++)
writeMemory(i, 1, &fillByte);
}
#endif
}
println("restored Tableobjects");
println("restored TableObjects");
}

void Memory::writeMemory()
Expand Down
Loading