diff --git a/examples/knx-linux/CMakeLists.txt b/examples/knx-linux/CMakeLists.txt index fe87d43..dc7e86f 100644 --- a/examples/knx-linux/CMakeLists.txt +++ b/examples/knx-linux/CMakeLists.txt @@ -19,13 +19,19 @@ add_executable(knx-linux ../../src/knx/bau.h ../../src/knx/bau07B0.cpp ../../src/knx/bau07B0.h - ../../src/knx/bau27B0.cpp + ../../src/knx/bau091A.cpp + ../../src/knx/bau091A.h + ../../src/knx/bau27B0.cpp ../../src/knx/bau27B0.h ../../src/knx/bau57B0.cpp ../../src/knx/bau57B0.h ../../src/knx/bau_systemB.cpp ../../src/knx/bau_systemB.h - ../../src/knx/bits.cpp + ../../src/knx/bau_systemB_device.cpp + ../../src/knx/bau_systemB_device.h + ../../src/knx/bau_systemB_coupler.cpp + ../../src/knx/bau_systemB_coupler.h + ../../src/knx/bits.cpp ../../src/knx/bits.h ../../src/knx/callback_property.h ../../src/knx/cemi_frame.cpp @@ -40,9 +46,9 @@ add_executable(knx-linux ../../src/knx/data_property.cpp ../../src/knx/data_property.h ../../src/knx/device_object.cpp - ../../src/knx/device_object.h - ../../src/knx/dpt.cpp - ../../src/knx/dpt.h + ../../src/knx/device_object.h + ../../src/knx/dpt.cpp + ../../src/knx/dpt.h ../../src/knx/dptconvert.cpp ../../src/knx/dptconvert.h ../../src/knx/function_property.h diff --git a/examples/knx-linux/main.cpp b/examples/knx-linux/main.cpp index f835e0e..66b4373 100644 --- a/examples/knx-linux/main.cpp +++ b/examples/knx-linux/main.cpp @@ -1,10 +1,7 @@ #include "knx_facade.h" -#include "knx/bau57B0.h" -#include "knx/bau27B0.h" -#include "knx/bau07B0.h" +#include "knx/bau091A.h" -#include "knx/group_object_table_object.h" #include "knx/bits.h" #include #include @@ -34,59 +31,17 @@ bool isSendHidReportPossible() return false; } #if MEDIUM_TYPE == 5 -KnxFacade knx; +KnxFacade knx; #elif MEDIUM_TYPE == 2 -KnxFacade knx; +KnxFacade knx; #else #error Only MEDIUM_TYPE IP and RF supported #endif -long lastsend = 0; - -#define CURR knx.getGroupObject(1) -#define MAX knx.getGroupObject(2) -#define MIN knx.getGroupObject(3) -#define RESET knx.getGroupObject(4) - -void measureTemp() -{ - long now = millis(); - if ((now - lastsend) < 10000) - return; - - lastsend = now; - int r = rand(); - double currentValue = (r * 1.0) / (RAND_MAX * 1.0); - currentValue *= 100; - currentValue -= 50; - // currentValue *= (670433.28 + 273); - // currentValue -= 273; - println(currentValue); - CURR.value(currentValue); - - double max = MAX.value(); - if (currentValue > max) - MAX.value(currentValue); - - if (currentValue < (double)MIN.value()) - MIN.value(currentValue); -} - -void resetCallback(GroupObject& go) -{ - if (go.value()) - { - MAX.valueNoSend(-273.0); - MIN.valueNoSend(670433.28); - } -} - void appLoop() { if (!knx.configured()) return; - - measureTemp(); } void setup() @@ -99,18 +54,7 @@ void setup() if (knx.configured()) { - CURR.dataPointType(Dpt(9, 1)); - MIN.dataPointType(Dpt(9, 1)); - MIN.value(670433.28); - MAX.dataPointType(Dpt(9, 1)); - MAX.valueNoSend(-273.0); - RESET.dataPointType(Dpt(1, 15)); - RESET.callback(resetCallback); - printf("Timeout: %d\n", knx.paramWord(0)); - printf("Zykl. senden: %d\n", knx.paramByte(2)); - printf("Min/Max senden: %d\n", knx.paramByte(3)); - printf("Aenderung senden: %d\n", knx.paramByte(4)); - printf("Abgleich %d\n", knx.paramByte(5)); + printf("configured %d\n", knx.paramByte(5)); } else println("not configured"); diff --git a/src/knx/application_layer.cpp b/src/knx/application_layer.cpp index 36b2ce2..78aa798 100644 --- a/src/knx/application_layer.cpp +++ b/src/knx/application_layer.cpp @@ -10,8 +10,7 @@ const SecurityControl ApplicationLayer::noSecurity {.toolAccess=false, .dataSecurity=DataSecurity::none}; -ApplicationLayer::ApplicationLayer(AssociationTableObject& assocTable, BusAccessUnit& bau): - _assocTable(assocTable), _bau(bau) +ApplicationLayer::ApplicationLayer(BusAccessUnit& bau) : _bau(bau) { } @@ -20,6 +19,11 @@ void ApplicationLayer::transportLayer(TransportLayer& layer) _transportLayer = &layer; } +void ApplicationLayer::associationTableObject(AssociationTableObject& assocTable) +{ + _assocTable = &assocTable; +} + #pragma region TL Callbacks void ApplicationLayer::dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) @@ -29,6 +33,9 @@ void ApplicationLayer::dataGroupIndication(HopCountType hopType, Priority priori void ApplicationLayer::dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl) { + if (_assocTable == nullptr) + return; + uint8_t len = apdu.length(); uint8_t dataArray[len]; uint8_t* data = dataArray; @@ -45,8 +52,8 @@ void ApplicationLayer::dataGroupIndication(HopCountType hopType, Priority priori } uint16_t startIdx = 0; - int32_t asap = _assocTable.nextAsap(tsap, startIdx); - for (; asap != -1; asap = _assocTable.nextAsap(tsap, startIdx)) + int32_t asap = _assocTable->nextAsap(tsap, startIdx); + for (; asap != -1; asap = _assocTable->nextAsap(tsap, startIdx)) { switch (apdu.type()) { @@ -365,12 +372,15 @@ void ApplicationLayer::dataConnectedConfirm(uint16_t tsap, const SecurityControl #pragma endregion void ApplicationLayer::groupValueReadRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl) { + if (_assocTable == nullptr) + return; + _savedAsapReadRequest = asap; CemiFrame frame(1); APDU& apdu = frame.apdu(); apdu.type(GroupValueRead); - int32_t value = _assocTable.translateAsap(asap); + int32_t value = _assocTable->translateAsap(asap); if (value < 0) return; // there is no tsap in association table for this asap @@ -928,6 +938,9 @@ void ApplicationLayer::propertyExtDataSend(ApduType type, AckType ack, Priority void ApplicationLayer::groupValueSend(ApduType type, AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* data, uint8_t& dataLength) { + if (_assocTable == nullptr) + return; + CemiFrame frame(dataLength + 1); APDU& apdu = frame.apdu(); apdu.type(type); @@ -943,7 +956,7 @@ void ApplicationLayer::groupValueSend(ApduType type, AckType ack, uint16_t asap, memcpy(apdudata + 1, data, dataLength); } // no need to check if there is a tsap. This is a response, so the read got trough - uint16_t tsap = (uint16_t)_assocTable.translateAsap(asap); + uint16_t tsap = (uint16_t)_assocTable->translateAsap(asap); dataGroupRequest(ack, hopType, priority, tsap, apdu, secCtrl); dataGroupIndication(hopType, priority, tsap, apdu, secCtrl); } diff --git a/src/knx/application_layer.h b/src/knx/application_layer.h index 4234716..2f4439c 100644 --- a/src/knx/application_layer.h +++ b/src/knx/application_layer.h @@ -22,12 +22,14 @@ class ApplicationLayer * @param assocTable The AssociationTable is used to translate between asap (i.e. group objects) and group addresses. * @param bau methods are called here depending of the content of the APDU */ - ApplicationLayer(AssociationTableObject& assocTable, BusAccessUnit& bau); + ApplicationLayer(BusAccessUnit& bau); /** * Assigns the TransportLayer to which encoded APDU are submitted to. */ void transportLayer(TransportLayer& layer); + void associationTableObject(AssociationTableObject& assocTable); + // from transport layer // Note: without data secure feature, the application layer is just used with SecurtyControl.dataSecurity = none // hooks that can be implemented by derived class (e.g. SecureApplicationLayer) @@ -204,7 +206,7 @@ class ApplicationLayer uint16_t _savedAsapReadRequest; uint16_t _savedAsapWriteRequest; uint16_t _savedAsapResponse; - AssociationTableObject& _assocTable; + AssociationTableObject* _assocTable = nullptr; BusAccessUnit& _bau; int32_t _connectedTsap = -1; diff --git a/src/knx/bau07B0.cpp b/src/knx/bau07B0.cpp index 47c4f34..ce4f056 100644 --- a/src/knx/bau07B0.cpp +++ b/src/knx/bau07B0.cpp @@ -8,12 +8,14 @@ using namespace std; Bau07B0::Bau07B0(Platform& platform) - : BauSystemB(platform), - _dlLayer(_deviceObj, _addrTable, _netLayer, _platform) + : BauSystemBDevice(platform), + _dlLayer(_deviceObj, _netLayer, _platform) #ifdef USE_CEMI_SERVER , _cemiServer(*this) #endif { + _dlLayer.groupAddressTable(_addrTable); + _netLayer.dataLinkLayer(_dlLayer); #ifdef USE_CEMI_SERVER _cemiServer.dataLinkLayer(_dlLayer); diff --git a/src/knx/bau07B0.h b/src/knx/bau07B0.h index a886d17..3e10955 100644 --- a/src/knx/bau07B0.h +++ b/src/knx/bau07B0.h @@ -1,14 +1,14 @@ #pragma once #include "config.h" -#include "bau_systemB.h" +#include "bau_systemB_device.h" #include "tpuart_data_link_layer.h" #include "cemi_server.h" #include "cemi_server_object.h" #ifdef USE_TP -class Bau07B0 : public BauSystemB +class Bau07B0 : public BauSystemBDevice { public: Bau07B0(Platform& platform); @@ -26,4 +26,4 @@ class Bau07B0 : public BauSystemB CemiServerObject _cemiServerObject; #endif }; -#endif \ No newline at end of file +#endif diff --git a/src/knx/bau091A.cpp b/src/knx/bau091A.cpp new file mode 100644 index 0000000..c958bc6 --- /dev/null +++ b/src/knx/bau091A.cpp @@ -0,0 +1,121 @@ +#include "config.h" +#include "bau091A.h" +#include "bits.h" +#include +#include + +#ifdef USE_IP + +using namespace std; + +Bau091A::Bau091A(Platform& platform) + : BauSystemBCoupler(platform), + _ipParameters(_deviceObj, platform), + _dlLayerPrimary(_deviceObj, _ipParameters, _netLayer, _platform), + _dlLayerSecondary(_deviceObj, _netLayer, platform) +#ifdef USE_CEMI_SERVER + , + _cemiServer(*this) +#endif +{ + _netLayer.dataLinkLayer(_dlLayerSecondary); +#ifdef USE_CEMI_SERVER + _cemiServer.dataLinkLayer(_dlLayer); + _dlLayer.cemiServer(_cemiServer); + _memory.addSaveRestore(&_cemiServerObject); +#endif + _memory.addSaveRestore(&_ipParameters); + + // Set Mask Version in Device Object depending on the BAU + _deviceObj.maskVersion(0x091A); + + // Set which interface objects are available in the device object + // This differs from BAU to BAU with different medium types. + // See PID_IO_LIST + Property* prop = _deviceObj.property(PID_IO_LIST); + prop->write(1, (uint16_t) OT_DEVICE); + prop->write(2, (uint16_t) OT_ADDR_TABLE); + prop->write(3, (uint16_t) OT_ASSOC_TABLE); + prop->write(4, (uint16_t) OT_GRP_OBJ_TABLE); + prop->write(5, (uint16_t) OT_APPLICATION_PROG); + prop->write(6, (uint16_t) OT_IP_PARAMETER); +#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER) + prop->write(7, (uint16_t) OT_SECURITY); + prop->write(8, (uint16_t) OT_CEMI_SERVER); +#elif defined(USE_DATASECURE) + prop->write(7, (uint16_t) OT_SECURITY); +#elif defined(USE_CEMI_SERVER) + prop->write(7, (uint16_t) OT_CEMI_SERVER); +#endif +} + +InterfaceObject* Bau091A::getInterfaceObject(uint8_t idx) +{ + switch (idx) + { + case 0: + return &_deviceObj; + case 4: + return &_appProgram; + case 5: // would be app_program 2 + return nullptr; + case 6: + return &_ipParameters; +#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER) + case 7: + return &_secIfObj; + case 8: + return &_cemiServerObject; +#elif defined(USE_CEMI_SERVER) + case 7: + return &_cemiServerObject; +#elif defined(USE_DATASECURE) + case 7: + return &_secIfObj; +#endif + default: + return nullptr; + } +} + +InterfaceObject* Bau091A::getInterfaceObject(ObjectType objectType, uint8_t objectInstance) +{ + // We do not use it right now. + // Required for coupler mode as there are multiple router objects for example + (void) objectInstance; + + switch (objectType) + { + case OT_DEVICE: + return &_deviceObj; + case OT_APPLICATION_PROG: + return &_appProgram; + case OT_IP_PARAMETER: + return &_ipParameters; +#ifdef USE_DATASECURE + case OT_SECURITY: + return &_secIfObj; +#endif +#ifdef USE_CEMI_SERVER + case OT_CEMI_SERVER: + return &_cemiServerObject; +#endif + default: + return nullptr; + } +} + +void Bau091A::doMasterReset(EraseCode eraseCode, uint8_t channel) +{ + // Common SystemB objects + BauSystemB::doMasterReset(eraseCode, channel); + + _ipParameters.masterReset(eraseCode, channel); +} + +DataLinkLayer& Bau091A::dataLinkLayer() +{ + return _dlLayerSecondary; +} + +#endif diff --git a/src/knx/bau091A.h b/src/knx/bau091A.h new file mode 100644 index 0000000..0d1c687 --- /dev/null +++ b/src/knx/bau091A.h @@ -0,0 +1,31 @@ +#pragma once + +#include "config.h" +#ifdef USE_IP +#include "bau_systemB_coupler.h" +#include "ip_parameter_object.h" +#include "ip_data_link_layer.h" +#include "tpuart_data_link_layer.h" +#include "cemi_server_object.h" + +class Bau091A : public BauSystemBCoupler +{ + public: + Bau091A(Platform& platform); + + protected: + InterfaceObject* getInterfaceObject(uint8_t idx); + InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance); + DataLinkLayer& dataLinkLayer(); + + virtual void doMasterReset(EraseCode eraseCode, uint8_t channel) override; + private: + IpParameterObject _ipParameters; + IpDataLinkLayer _dlLayerPrimary; + TpUartDataLinkLayer _dlLayerSecondary; +#ifdef USE_CEMI_SERVER + CemiServer _cemiServer; + CemiServerObject _cemiServerObject; +#endif +}; +#endif diff --git a/src/knx/bau27B0.cpp b/src/knx/bau27B0.cpp index 0ef567a..5def14b 100644 --- a/src/knx/bau27B0.cpp +++ b/src/knx/bau27B0.cpp @@ -7,8 +7,8 @@ using namespace std; Bau27B0::Bau27B0(Platform& platform) - : BauSystemB(platform), - _dlLayer(_deviceObj, _rfMediumObj, _addrTable, _netLayer, _platform) + : BauSystemBDevice(platform), + _dlLayer(_deviceObj, _rfMediumObj, _netLayer, _platform) #ifdef USE_CEMI_SERVER , _cemiServer(*this) #endif diff --git a/src/knx/bau27B0.h b/src/knx/bau27B0.h index 63f2844..036d207 100644 --- a/src/knx/bau27B0.h +++ b/src/knx/bau27B0.h @@ -2,14 +2,14 @@ #include "config.h" #ifdef USE_RF -#include "bau_systemB.h" +#include "bau_systemB_device.h" #include "rf_medium_object.h" #include "rf_physical_layer.h" #include "rf_data_link_layer.h" #include "cemi_server.h" #include "cemi_server_object.h" -class Bau27B0 : public BauSystemB +class Bau27B0 : public BauSystemBDevice { public: Bau27B0(Platform& platform); diff --git a/src/knx/bau57B0.cpp b/src/knx/bau57B0.cpp index eee3cc2..eb37ef0 100644 --- a/src/knx/bau57B0.cpp +++ b/src/knx/bau57B0.cpp @@ -9,9 +9,9 @@ using namespace std; Bau57B0::Bau57B0(Platform& platform) - : BauSystemB(platform), + : BauSystemBDevice(platform), _ipParameters(_deviceObj, platform), - _dlLayer(_deviceObj, _addrTable, _ipParameters, _netLayer, _platform) + _dlLayer(_deviceObj, _ipParameters, _netLayer, _platform) #ifdef USE_CEMI_SERVER , _cemiServer(*this) diff --git a/src/knx/bau57B0.h b/src/knx/bau57B0.h index 85c5ca0..1066ab7 100644 --- a/src/knx/bau57B0.h +++ b/src/knx/bau57B0.h @@ -2,12 +2,12 @@ #include "config.h" #ifdef USE_IP -#include "bau_systemB.h" +#include "bau_systemB_device.h" #include "ip_parameter_object.h" #include "ip_data_link_layer.h" #include "cemi_server_object.h" -class Bau57B0 : public BauSystemB +class Bau57B0 : public BauSystemBDevice { public: Bau57B0(Platform& platform); diff --git a/src/knx/bau_systemB.cpp b/src/knx/bau_systemB.cpp index 8d20f59..cedccdc 100644 --- a/src/knx/bau_systemB.cpp +++ b/src/knx/bau_systemB.cpp @@ -14,41 +14,17 @@ enum NmReadSerialNumberType static constexpr auto kFunctionPropertyResultBufferMaxSize = 64; static constexpr auto kRestartProcessTime = 3; -BauSystemB::BauSystemB(Platform& platform): _memory(platform, _deviceObj), _addrTable(_memory), - _assocTable(_memory), _groupObjTable(_memory), _appProgram(_memory), - _platform(platform), -#ifdef USE_DATASECURE - _appLayer(_deviceObj, _secIfObj, _assocTable, _addrTable, *this), -#else - _appLayer(_assocTable, *this), -#endif - _transLayer(_appLayer, _addrTable), _netLayer(_transLayer) +BauSystemB::BauSystemB(Platform& platform): _memory(platform, _deviceObj), + _appProgram(_memory), + _platform(platform) { -#ifdef USE_DATASECURE - _secIfObj.secureApplicationLayer(_appLayer); -#endif - _appLayer.transportLayer(_transLayer); - _transLayer.networkLayer(_netLayer); - _memory.addSaveRestore(&_deviceObj); _memory.addSaveRestore(&_appProgram); - _memory.addSaveRestore(&_addrTable); - _memory.addSaveRestore(&_assocTable); - _memory.addSaveRestore(&_groupObjTable); -#ifdef USE_DATASECURE - _memory.addSaveRestore(&_secIfObj); -#endif - } void BauSystemB::loop() { dataLinkLayer().loop(); - _transLayer.loop(); - sendNextGroupTelegram(); nextRestartState(); -#ifdef USE_DATASECURE - _appLayer.loop(); -#endif } bool BauSystemB::enabled() @@ -61,74 +37,6 @@ void BauSystemB::enabled(bool value) dataLinkLayer().enabled(value); } -void BauSystemB::sendNextGroupTelegram() -{ - if(!configured()) - return; - - static uint16_t startIdx = 1; - - GroupObjectTableObject& table = _groupObjTable; - uint16_t objCount = table.entryCount(); - - for (uint16_t asap = startIdx; asap <= objCount; asap++) - { - GroupObject& go = table.get(asap); - - ComFlag flag = go.commFlag(); - if (flag != ReadRequest && flag != WriteRequest) - continue; - - if (!go.communicationEnable()) - continue; - - SecurityControl goSecurity; - goSecurity.toolAccess = false; // Secured group communication never uses the toolkey. ETS knows all keys, also the group keys. - -#ifdef USE_DATASECURE - // Get security flags from Security Interface Object for this group object - goSecurity.dataSecurity = _secIfObj.getGroupObjectSecurity(asap); -#else - goSecurity.dataSecurity = DataSecurity::none; -#endif - - if (flag == WriteRequest && go.transmitEnable()) - { - uint8_t* data = go.valueRef(); - _appLayer.groupValueWriteRequest(AckRequested, asap, go.priority(), NetworkLayerParameter, goSecurity, data, - go.sizeInTelegram()); - } - else if (flag == ReadRequest) - { - _appLayer.groupValueReadRequest(AckRequested, asap, go.priority(), NetworkLayerParameter, goSecurity); - } - - go.commFlag(Transmitting); - - startIdx = asap + 1; - return; - } - - startIdx = 1; -} - -void BauSystemB::updateGroupObject(GroupObject & go, uint8_t * data, uint8_t length) -{ - uint8_t* goData = go.valueRef(); - if (length != go.valueSize()) - { - go.commFlag(Error); - return; - } - - memcpy(goData, data, length); - - go.commFlag(Updated); - GroupObjectUpdatedHandler handler = go.callback(); - if (handler) - handler(go); -} - void BauSystemB::readMemory() { _memory.readMemory(); @@ -139,21 +47,16 @@ void BauSystemB::writeMemory() _memory.writeMemory(); } -DeviceObject& BauSystemB::deviceObject() -{ - return _deviceObj; -} - -GroupObjectTableObject& BauSystemB::groupObjectTable() -{ - return _groupObjTable; -} - ApplicationProgramObject& BauSystemB::parameters() { return _appProgram; } +DeviceObject& BauSystemB::deviceObject() +{ + return _deviceObj; +} + bool BauSystemB::configured() { // _configured is set to true initially, if the device was configured with ETS it will be set to true after restart @@ -161,10 +64,7 @@ bool BauSystemB::configured() if (!_configured) return false; - _configured = _groupObjTable.loadState() == LS_LOADED - && _addrTable.loadState() == LS_LOADED - && _assocTable.loadState() == LS_LOADED - && _appProgram.loadState() == LS_LOADED; + _configured = _appProgram.loadState() == LS_LOADED; return _configured; } @@ -233,7 +133,7 @@ void BauSystemB::deviceDescriptorReadIndication(Priority priority, HopCountType uint8_t data[2]; pushWord(_deviceObj.maskVersion(), data); - _appLayer.deviceDescriptorReadResponse(AckRequested, priority, hopType, asap, secCtrl, descriptorType, data); + applicationLayer().deviceDescriptorReadResponse(AckRequested, priority, hopType, asap, secCtrl, descriptorType, data); } void BauSystemB::memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, @@ -248,7 +148,7 @@ void BauSystemB::memoryWriteIndication(Priority priority, HopCountType hopType, void BauSystemB::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress) { - _appLayer.memoryReadResponse(AckRequested, priority, hopType, asap, secCtrl, number, memoryAddress, + applicationLayer().memoryReadResponse(AckRequested, priority, hopType, asap, secCtrl, number, memoryAddress, _memory.toAbsolute(memoryAddress)); } @@ -256,27 +156,17 @@ void BauSystemB::memoryExtWriteIndication(Priority priority, HopCountType hopTyp { _memory.writeMemory(memoryAddress, number, data); - _appLayer.memoryExtWriteResponse(AckRequested, priority, hopType, asap, secCtrl, ReturnCodes::Success, number, memoryAddress, _memory.toAbsolute(memoryAddress)); + applicationLayer().memoryExtWriteResponse(AckRequested, priority, hopType, asap, secCtrl, ReturnCodes::Success, number, memoryAddress, _memory.toAbsolute(memoryAddress)); } void BauSystemB::memoryExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress) { - _appLayer.memoryExtReadResponse(AckRequested, priority, hopType, asap, secCtrl, ReturnCodes::Success, number, memoryAddress, _memory.toAbsolute(memoryAddress)); + applicationLayer().memoryExtReadResponse(AckRequested, priority, hopType, asap, secCtrl, ReturnCodes::Success, number, memoryAddress, _memory.toAbsolute(memoryAddress)); } void BauSystemB::doMasterReset(EraseCode eraseCode, uint8_t channel) { - _addrTable.masterReset(eraseCode, channel); - _assocTable.masterReset(eraseCode, channel); - _groupObjTable.masterReset(eraseCode, channel); _appProgram.masterReset(eraseCode, channel); -#ifdef USE_DATASECURE - // If erase code is FactoryReset or FactoryResetWithoutIA, set FDSK as toolkey again - // and disable security mode. - // FIXME: the A_RestartResponse PDU has still to be sent with the current toolkey. - // Idea: use local confirmation of sent A_RestartResponse PDU to trigger writing the FDSK afterwards - _secIfObj.masterReset(eraseCode, channel); -#endif } void BauSystemB::restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, RestartType restartType, EraseCode eraseCode, uint8_t channel) @@ -290,7 +180,7 @@ void BauSystemB::restartRequestIndication(Priority priority, HopCountType hopTyp uint8_t errorCode = checkmasterResetValidity(eraseCode, channel); // We send the restart response now before actually applying the reset values // Processing time is kRestartProcessTime (example 3 seconds) that we require for the applying the master reset with restart - _appLayer.restartResponse(AckRequested, priority, hopType, secCtrl, errorCode, (errorCode == 0) ? kRestartProcessTime : 0); + applicationLayer().restartResponse(AckRequested, priority, hopType, secCtrl, errorCode, (errorCode == 0) ? kRestartProcessTime : 0); doMasterReset(eraseCode, channel); } else @@ -307,12 +197,12 @@ void BauSystemB::restartRequestIndication(Priority priority, HopCountType hopTyp void BauSystemB::authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint32_t key) { - _appLayer.authorizeResponse(AckRequested, priority, hopType, asap, secCtrl, 0); + applicationLayer().authorizeResponse(AckRequested, priority, hopType, asap, secCtrl, 0); } void BauSystemB::userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress) { - _appLayer.userMemoryReadResponse(AckRequested, priority, hopType, asap, secCtrl, number, memoryAddress, + applicationLayer().userMemoryReadResponse(AckRequested, priority, hopType, asap, secCtrl, number, memoryAddress, _memory.toAbsolute(memoryAddress)); } @@ -336,7 +226,7 @@ void BauSystemB::propertyDescriptionReadIndication(Priority priority, HopCountTy if (obj) obj->readPropertyDescription(pid, propertyIndex, writeEnable, type, numberOfElements, access); - _appLayer.propertyDescriptionReadResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, pid, propertyIndex, + applicationLayer().propertyDescriptionReadResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, pid, propertyIndex, writeEnable, type, numberOfElements, access); } @@ -362,7 +252,7 @@ void BauSystemB::propertyValueExtWriteIndication(Priority priority, HopCountType if (confirmed) { - _appLayer.propertyValueExtWriteConResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, numberOfElements, startIndex, returnCode); + applicationLayer().propertyValueExtWriteConResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, numberOfElements, startIndex, returnCode); } } @@ -390,7 +280,7 @@ void BauSystemB::propertyValueReadIndication(Priority priority, HopCountType hop if (elementCount == 0) size = 0; - _appLayer.propertyValueReadResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, propertyId, elementCount, + applicationLayer().propertyValueReadResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, propertyId, elementCount, startIndex, data, size); } @@ -418,7 +308,7 @@ void BauSystemB::propertyValueExtReadIndication(Priority priority, HopCountType if (elementCount == 0) size = 0; - _appLayer.propertyValueExtReadResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, elementCount, + applicationLayer().propertyValueExtReadResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, elementCount, startIndex, data, size); } @@ -441,7 +331,7 @@ void BauSystemB::functionPropertyCommandIndication(Priority priority, HopCountTy } } - _appLayer.functionPropertyStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, propertyId, resultData, resultLength); + applicationLayer().functionPropertyStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, propertyId, resultData, resultLength); } void BauSystemB::functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, @@ -463,7 +353,7 @@ void BauSystemB::functionPropertyStateIndication(Priority priority, HopCountType } } - _appLayer.functionPropertyStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, propertyId, resultData, resultLength); + applicationLayer().functionPropertyStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, propertyId, resultData, resultLength); } void BauSystemB::functionPropertyExtCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, @@ -519,7 +409,7 @@ void BauSystemB::functionPropertyExtCommandIndication(Priority priority, HopCoun resultData[0] = ReturnCodes::GenericError; } - _appLayer.functionPropertyExtStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, resultData, resultLength); + applicationLayer().functionPropertyExtStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, resultData, resultLength); } void BauSystemB::functionPropertyExtStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance, @@ -566,13 +456,13 @@ void BauSystemB::functionPropertyExtStateIndication(Priority priority, HopCountT resultData[0] = ReturnCodes::GenericError; } - _appLayer.functionPropertyExtStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, resultData, resultLength); + applicationLayer().functionPropertyExtStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, resultData, resultLength); } void BauSystemB::individualAddressReadIndication(HopCountType hopType, const SecurityControl &secCtrl) { if (_deviceObj.progMode()) - _appLayer.individualAddressReadResponse(AckRequested, hopType, secCtrl); + applicationLayer().individualAddressReadResponse(AckRequested, hopType, secCtrl); } void BauSystemB::individualAddressWriteIndication(HopCountType hopType, const SecurityControl &secCtrl, uint16_t newaddress) @@ -598,85 +488,10 @@ void BauSystemB::individualAddressSerialNumberReadIndication(Priority priority, if (!memcmp(knxSerialNumber, _deviceObj.propertyData(PID_SERIAL_NUMBER), 6)) { uint8_t emptyDomainAddress[6] = {0x00}; - _appLayer.IndividualAddressSerialNumberReadResponse(priority, hopType, secCtrl, emptyDomainAddress, knxSerialNumber); + applicationLayer().IndividualAddressSerialNumberReadResponse(priority, hopType, secCtrl, emptyDomainAddress, knxSerialNumber); } } -void BauSystemB::groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t * data, uint8_t dataLength, bool status) -{ - GroupObject& go = _groupObjTable.get(asap); - if (status) - go.commFlag(Ok); - else - go.commFlag(Error); -} - -void BauSystemB::groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, bool status) -{ - GroupObject& go = _groupObjTable.get(asap); - if (status) - go.commFlag(Ok); - else - go.commFlag(Error); -} - -void BauSystemB::groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl) -{ -#ifdef USE_DATASECURE - DataSecurity requiredGoSecurity; - - // Get security flags from Security Interface Object for this group object - requiredGoSecurity = _secIfObj.getGroupObjectSecurity(asap); - - if (secCtrl.dataSecurity != requiredGoSecurity) - { - println("GroupValueRead: access denied due to wrong security flags"); - return; - } -#endif - - GroupObject& go = _groupObjTable.get(asap); - - if (!go.communicationEnable() || !go.readEnable()) - return; - - uint8_t* data = go.valueRef(); - _appLayer.groupValueReadResponse(AckRequested, asap, priority, hopType, secCtrl, data, go.sizeInTelegram()); -} - -void BauSystemB::groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* data, - uint8_t dataLength) -{ - GroupObject& go = _groupObjTable.get(asap); - - if (!go.communicationEnable() || !go.responseUpdateEnable()) - return; - - updateGroupObject(go, data, dataLength); -} - -void BauSystemB::groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t * data, uint8_t dataLength) -{ -#ifdef USE_DATASECURE - DataSecurity requiredGoSecurity; - - // Get security flags from Security Interface Object for this group object - requiredGoSecurity = _secIfObj.getGroupObjectSecurity(asap); - - if (secCtrl.dataSecurity != requiredGoSecurity) - { - println("GroupValueWrite: access denied due to wrong security flags"); - return; - } -#endif - GroupObject& go = _groupObjTable.get(asap); - - if (!go.communicationEnable() || !go.writeEnable()) - return; - - updateGroupObject(go, data, dataLength); -} - void BauSystemB::addSaveRestore(SaveRestore* obj) { _memory.addSaveRestore(obj); @@ -684,12 +499,12 @@ void BauSystemB::addSaveRestore(SaveRestore* obj) bool BauSystemB::restartRequest(uint16_t asap, const SecurityControl secCtrl) { - if (_appLayer.isConnected()) + if (applicationLayer().isConnected()) return false; _restartState = Connecting; // order important, has to be set BEFORE connectRequest _restartSecurity = secCtrl; - _appLayer.connectRequest(asap, SystemPriority); - _appLayer.deviceDescriptorReadRequest(AckRequested, SystemPriority, NetworkLayerParameter, asap, secCtrl, 0); + applicationLayer().connectRequest(asap, SystemPriority); + applicationLayer().deviceDescriptorReadRequest(AckRequested, SystemPriority, NetworkLayerParameter, asap, secCtrl, 0); return true; } @@ -721,7 +536,7 @@ void BauSystemB::nextRestartState() /* connection confirmed, we send restartRequest, but we wait a moment (sending ACK etc)... */ if (millis() - _restartDelay > 30) { - _appLayer.restartRequest(AckRequested, SystemPriority, NetworkLayerParameter, _restartSecurity); + applicationLayer().restartRequest(AckRequested, SystemPriority, NetworkLayerParameter, _restartSecurity); _restartState = Restarted; _restartDelay = millis(); } @@ -730,7 +545,7 @@ void BauSystemB::nextRestartState() /* restart is finished, we send a discommect */ if (millis() - _restartDelay > 30) { - _appLayer.disconnectRequest(SystemPriority); + applicationLayer().disconnectRequest(SystemPriority); _restartState = Idle; } default: @@ -753,7 +568,7 @@ void BauSystemB::systemNetworkParameterReadIndication(Priority priority, HopCoun if (_deviceObj.progMode() && (objectType == OT_DEVICE) && (propertyId == PID_SERIAL_NUMBER)) { // Send reply. testResult data is KNX serial number - _appLayer.systemNetworkParameterReadResponse(priority, hopType, secCtrl, objectType, propertyId, + applicationLayer().systemNetworkParameterReadResponse(priority, hopType, secCtrl, objectType, propertyId, testInfo, testInfoLength, (uint8_t*)_deviceObj.propertyData(PID_SERIAL_NUMBER), 6); } break; diff --git a/src/knx/bau_systemB.h b/src/knx/bau_systemB.h index b485942..7ae6318 100644 --- a/src/knx/bau_systemB.h +++ b/src/knx/bau_systemB.h @@ -2,10 +2,6 @@ #include "config.h" #include "bau.h" -#include "device_object.h" -#include "address_table_object.h" -#include "association_table_object.h" -#include "group_object_table_object.h" #include "security_interface_object.h" #include "application_program_object.h" #include "application_layer.h" @@ -21,9 +17,8 @@ class BauSystemB : protected BusAccessUnit public: BauSystemB(Platform& platform); virtual void loop(); - DeviceObject& deviceObject(); - GroupObjectTableObject& groupObjectTable(); ApplicationProgramObject& parameters(); + DeviceObject& deviceObject(); Memory& memory(); bool configured(); bool enabled(); @@ -43,6 +38,8 @@ class BauSystemB : protected BusAccessUnit protected: virtual DataLinkLayer& dataLinkLayer() = 0; + virtual ApplicationLayer& applicationLayer() = 0; + void memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data) override; void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, @@ -80,14 +77,6 @@ class BauSystemB : protected BusAccessUnit void individualAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t newIndividualAddress, uint8_t* knxSerialNumber) override; void individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* knxSerialNumber) override; - void groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, - uint8_t* data, uint8_t dataLength, bool status) override; - void groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, bool status) override; - void groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl) override; - void groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, - uint8_t* data, uint8_t dataLength) override; - void groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, - uint8_t* data, uint8_t dataLength) override; void systemNetworkParameterReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t objectType, uint16_t propertyId, uint8_t* testInfo, uint16_t testinfoLength) override; void systemNetworkParameterReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint16_t objectType, @@ -96,8 +85,6 @@ class BauSystemB : protected BusAccessUnit virtual InterfaceObject* getInterfaceObject(uint8_t idx) = 0; virtual InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance) = 0; - void sendNextGroupTelegram(); - void updateGroupObject(GroupObject& go, uint8_t* data, uint8_t length); void nextRestartState(); virtual void doMasterReset(EraseCode eraseCode, uint8_t channel); @@ -110,21 +97,10 @@ class BauSystemB : protected BusAccessUnit Restarted }; - DeviceObject _deviceObj; Memory _memory; - AddressTableObject _addrTable; - AssociationTableObject _assocTable; - GroupObjectTableObject _groupObjTable; + DeviceObject _deviceObj; ApplicationProgramObject _appProgram; Platform& _platform; -#ifdef USE_DATASECURE - SecureApplicationLayer _appLayer; - SecurityInterfaceObject _secIfObj; -#else - ApplicationLayer _appLayer; -#endif - TransportLayer _transLayer; - NetworkLayer _netLayer; bool _configured = true; RestartState _restartState = Idle; SecurityControl _restartSecurity; diff --git a/src/knx/bau_systemB_coupler.cpp b/src/knx/bau_systemB_coupler.cpp new file mode 100644 index 0000000..0f7c1b5 --- /dev/null +++ b/src/knx/bau_systemB_coupler.cpp @@ -0,0 +1,57 @@ +#include "bau_systemB_coupler.h" +#include "bits.h" +#include +#include + +BauSystemBCoupler::BauSystemBCoupler(Platform& platform) : + BauSystemB(platform), + _platform(platform), +#ifdef USE_DATASECURE + _appLayer(_deviceObj, _secIfObj, *this), +#else + _appLayer(*this), +#endif + _transLayer(_appLayer), _netLayer(_deviceObj, _transLayer) +{ +#ifdef USE_DATASECURE + _secIfObj.secureApplicationLayer(_appLayer); +#endif + _appLayer.transportLayer(_transLayer); + _transLayer.networkLayer(_netLayer); + _memory.addSaveRestore(&_deviceObj); + _memory.addSaveRestore(&_appProgram); +#ifdef USE_DATASECURE + _memory.addSaveRestore(&_secIfObj); +#endif + +} + +ApplicationLayer& BauSystemBCoupler::applicationLayer() +{ + return _appLayer; +} + +void BauSystemBCoupler::loop() +{ + dataLinkLayer().loop(); + _transLayer.loop(); +#ifdef USE_DATASECURE + _appLayer.loop(); +#endif +} + +bool BauSystemBCoupler::configured() +{ + // _configured is set to true initially, if the device was configured with ETS it will be set to true after restart + + if (!_configured) + return false; + + _configured = _appProgram.loadState() == LS_LOADED; + + return _configured; +} + +void BauSystemBCoupler::doMasterReset(EraseCode eraseCode, uint8_t channel) +{ +} diff --git a/src/knx/bau_systemB_coupler.h b/src/knx/bau_systemB_coupler.h new file mode 100644 index 0000000..e7b66dd --- /dev/null +++ b/src/knx/bau_systemB_coupler.h @@ -0,0 +1,53 @@ +#pragma once + +#include "config.h" +#include "bau_systemB.h" +#include "device_object.h" +#include "address_table_object.h" +#include "association_table_object.h" +#include "group_object_table_object.h" +#include "security_interface_object.h" +#include "application_program_object.h" +#include "application_layer.h" +#include "secure_application_layer.h" +#include "transport_layer.h" +#include "network_layer.h" +#include "data_link_layer.h" +#include "platform.h" +#include "memory.h" + +class BauSystemBCoupler : public BauSystemB +{ + public: + BauSystemBCoupler(Platform& platform); + virtual void loop(); + bool configured(); + + protected: + virtual DataLinkLayer& dataLinkLayer() = 0; + virtual ApplicationLayer& applicationLayer() override; + + virtual void doMasterReset(EraseCode eraseCode, uint8_t channel); + + enum RestartState + { + Idle, + Connecting, + Connected, + Restarted + }; + + Platform& _platform; +#ifdef USE_DATASECURE + SecureApplicationLayer _appLayer; + SecurityInterfaceObject _secIfObj; +#else + ApplicationLayer _appLayer; +#endif + TransportLayer _transLayer; + NetworkLayer _netLayer; + bool _configured = true; + RestartState _restartState = Idle; + SecurityControl _restartSecurity; + uint32_t _restartDelay = 0; +}; diff --git a/src/knx/bau_systemB_device.cpp b/src/knx/bau_systemB_device.cpp new file mode 100644 index 0000000..8f7a574 --- /dev/null +++ b/src/knx/bau_systemB_device.cpp @@ -0,0 +1,227 @@ +#include "bau_systemB_device.h" +#include "bits.h" +#include +#include + +BauSystemBDevice::BauSystemBDevice(Platform& platform) : + BauSystemB(platform), + _addrTable(_memory), + _assocTable(_memory), _groupObjTable(_memory), +#ifdef USE_DATASECURE + _appLayer(_deviceObj, _secIfObj, *this), +#else + _appLayer(*this), +#endif + _transLayer(_appLayer), _netLayer(_deviceObj, _transLayer) +{ +#ifdef USE_DATASECURE + _secIfObj.secureApplicationLayer(_appLayer); +#endif + _appLayer.transportLayer(_transLayer); + _appLayer.associationTableObject(_assocTable); + _appLayer.groupAddressTable(_addrTable); + _transLayer.networkLayer(_netLayer); + _transLayer.groupAddressTable(_addrTable); + + _memory.addSaveRestore(&_deviceObj); + _memory.addSaveRestore(&_addrTable); + _memory.addSaveRestore(&_assocTable); + _memory.addSaveRestore(&_groupObjTable); +#ifdef USE_DATASECURE + _memory.addSaveRestore(&_secIfObj); +#endif +} + +ApplicationLayer& BauSystemBDevice::applicationLayer() +{ + return _appLayer; +} + +void BauSystemBDevice::loop() +{ + dataLinkLayer().loop(); + _transLayer.loop(); + sendNextGroupTelegram(); + nextRestartState(); +#ifdef USE_DATASECURE + _appLayer.loop(); +#endif +} + +void BauSystemBDevice::sendNextGroupTelegram() +{ + if(!configured()) + return; + + static uint16_t startIdx = 1; + + GroupObjectTableObject& table = _groupObjTable; + uint16_t objCount = table.entryCount(); + + for (uint16_t asap = startIdx; asap <= objCount; asap++) + { + GroupObject& go = table.get(asap); + + ComFlag flag = go.commFlag(); + if (flag != ReadRequest && flag != WriteRequest) + continue; + + if (!go.communicationEnable()) + continue; + + SecurityControl goSecurity; + goSecurity.toolAccess = false; // Secured group communication never uses the toolkey. ETS knows all keys, also the group keys. + +#ifdef USE_DATASECURE + // Get security flags from Security Interface Object for this group object + goSecurity.dataSecurity = _secIfObj.getGroupObjectSecurity(asap); +#else + goSecurity.dataSecurity = DataSecurity::none; +#endif + + if (flag == WriteRequest && go.transmitEnable()) + { + uint8_t* data = go.valueRef(); + _appLayer.groupValueWriteRequest(AckRequested, asap, go.priority(), NetworkLayerParameter, goSecurity, data, + go.sizeInTelegram()); + } + else if (flag == ReadRequest) + { + _appLayer.groupValueReadRequest(AckRequested, asap, go.priority(), NetworkLayerParameter, goSecurity); + } + + go.commFlag(Transmitting); + + startIdx = asap + 1; + return; + } + + startIdx = 1; +} + +void BauSystemBDevice::updateGroupObject(GroupObject & go, uint8_t * data, uint8_t length) +{ + uint8_t* goData = go.valueRef(); + if (length != go.valueSize()) + { + go.commFlag(Error); + return; + } + + memcpy(goData, data, length); + + go.commFlag(Updated); + GroupObjectUpdatedHandler handler = go.callback(); + if (handler) + handler(go); +} + +GroupObjectTableObject& BauSystemBDevice::groupObjectTable() +{ + return _groupObjTable; +} + +bool BauSystemBDevice::configured() +{ + // _configured is set to true initially, if the device was configured with ETS it will be set to true after restart + + if (!_configured) + return false; + + _configured = _groupObjTable.loadState() == LS_LOADED + && _addrTable.loadState() == LS_LOADED + && _assocTable.loadState() == LS_LOADED + && _appProgram.loadState() == LS_LOADED; + + return _configured; +} + +void BauSystemBDevice::doMasterReset(EraseCode eraseCode, uint8_t channel) +{ + _addrTable.masterReset(eraseCode, channel); + _assocTable.masterReset(eraseCode, channel); + _groupObjTable.masterReset(eraseCode, channel); + _appProgram.masterReset(eraseCode, channel); +#ifdef USE_DATASECURE + // If erase code is FactoryReset or FactoryResetWithoutIA, set FDSK as toolkey again + // and disable security mode. + // FIXME: the A_RestartResponse PDU has still to be sent with the current toolkey. + // Idea: use local confirmation of sent A_RestartResponse PDU to trigger writing the FDSK afterwards + _secIfObj.masterReset(eraseCode, channel); +#endif +} + +void BauSystemBDevice::groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t * data, uint8_t dataLength, bool status) +{ + GroupObject& go = _groupObjTable.get(asap); + if (status) + go.commFlag(Ok); + else + go.commFlag(Error); +} + +void BauSystemBDevice::groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, bool status) +{ + GroupObject& go = _groupObjTable.get(asap); + if (status) + go.commFlag(Ok); + else + go.commFlag(Error); +} + +void BauSystemBDevice::groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl) +{ +#ifdef USE_DATASECURE + DataSecurity requiredGoSecurity; + + // Get security flags from Security Interface Object for this group object + requiredGoSecurity = _secIfObj.getGroupObjectSecurity(asap); + + if (secCtrl.dataSecurity != requiredGoSecurity) + { + println("GroupValueRead: access denied due to wrong security flags"); + return; + } +#endif + + GroupObject& go = _groupObjTable.get(asap); + + if (!go.communicationEnable() || !go.readEnable()) + return; + + uint8_t* data = go.valueRef(); + _appLayer.groupValueReadResponse(AckRequested, asap, priority, hopType, secCtrl, data, go.sizeInTelegram()); +} + +void BauSystemBDevice::groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* data, + uint8_t dataLength) +{ + GroupObject& go = _groupObjTable.get(asap); + + if (!go.communicationEnable() || !go.responseUpdateEnable()) + return; + + updateGroupObject(go, data, dataLength); +} + +void BauSystemBDevice::groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t * data, uint8_t dataLength) +{ +#ifdef USE_DATASECURE + DataSecurity requiredGoSecurity; + + // Get security flags from Security Interface Object for this group object + requiredGoSecurity = _secIfObj.getGroupObjectSecurity(asap); + + if (secCtrl.dataSecurity != requiredGoSecurity) + { + println("GroupValueWrite: access denied due to wrong security flags"); + return; + } +#endif + GroupObject& go = _groupObjTable.get(asap); + + if (!go.communicationEnable() || !go.writeEnable()) + return; + + updateGroupObject(go, data, dataLength); +} diff --git a/src/knx/bau_systemB_device.h b/src/knx/bau_systemB_device.h new file mode 100644 index 0000000..29daac6 --- /dev/null +++ b/src/knx/bau_systemB_device.h @@ -0,0 +1,56 @@ +#pragma once + +#include "config.h" +#include "bau_systemB.h" +#include "device_object.h" +#include "address_table_object.h" +#include "association_table_object.h" +#include "group_object_table_object.h" +#include "security_interface_object.h" +#include "application_program_object.h" +#include "application_layer.h" +#include "secure_application_layer.h" +#include "transport_layer.h" +#include "network_layer.h" +#include "data_link_layer.h" +#include "platform.h" +#include "memory.h" + +class BauSystemBDevice : public BauSystemB +{ + public: + BauSystemBDevice(Platform& platform); + virtual void loop(); + GroupObjectTableObject& groupObjectTable(); + Memory& memory(); + bool configured(); + + protected: + virtual ApplicationLayer& applicationLayer() override; + + void groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, + uint8_t* data, uint8_t dataLength, bool status) override; + void groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, bool status) override; + void groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl) override; + void groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, + uint8_t* data, uint8_t dataLength) override; + void groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl &secCtrl, + uint8_t* data, uint8_t dataLength) override; + + void sendNextGroupTelegram(); + void updateGroupObject(GroupObject& go, uint8_t* data, uint8_t length); + + virtual void doMasterReset(EraseCode eraseCode, uint8_t channel); + + AddressTableObject _addrTable; + AssociationTableObject _assocTable; + GroupObjectTableObject _groupObjTable; +#ifdef USE_DATASECURE + SecureApplicationLayer _appLayer; + SecurityInterfaceObject _secIfObj; +#else + ApplicationLayer _appLayer; +#endif + TransportLayer _transLayer; + NetworkLayer _netLayer; +}; diff --git a/src/knx/data_link_layer.cpp b/src/knx/data_link_layer.cpp index 877783d..1d518f5 100644 --- a/src/knx/data_link_layer.cpp +++ b/src/knx/data_link_layer.cpp @@ -6,9 +6,8 @@ #include "address_table_object.h" #include "cemi_server.h" -DataLinkLayer::DataLinkLayer(DeviceObject& devObj, AddressTableObject& addrTab, - NetworkLayer& layer, Platform& platform) : - _deviceObject(devObj), _groupAddressTable(addrTab), _networkLayer(layer), _platform(platform) +DataLinkLayer::DataLinkLayer(DeviceObject& devObj, NetworkLayer& layer, Platform& platform) : + _deviceObject(devObj), _networkLayer(layer), _platform(platform) { } @@ -115,18 +114,6 @@ void DataLinkLayer::frameRecieved(CemiFrame& frame) } else { - if (addrType == InduvidualAddress && destination != _deviceObject.induvidualAddress()) - return; - - if (addrType == GroupAddress && !_groupAddressTable.contains(destination)) - return; - -// if (frame.npdu().octetCount() > 0) -// { -// _print("-> DLL "); -// frame.apdu().printPDU(); -// } - _networkLayer.dataIndication(ack, addrType, destination, type, npdu, priority, source); } } diff --git a/src/knx/data_link_layer.h b/src/knx/data_link_layer.h index 28ba679..be19402 100644 --- a/src/knx/data_link_layer.h +++ b/src/knx/data_link_layer.h @@ -4,7 +4,6 @@ #include #include "device_object.h" -#include "address_table_object.h" #include "knx_types.h" #include "network_layer.h" #include "cemi_server.h" @@ -12,7 +11,7 @@ class DataLinkLayer { public: - DataLinkLayer(DeviceObject& devObj, AddressTableObject& addrTab, NetworkLayer& layer, + DataLinkLayer(DeviceObject& devObj, NetworkLayer& layer, Platform& platform); #ifdef USE_CEMI_SERVER @@ -36,7 +35,6 @@ class DataLinkLayer virtual bool sendFrame(CemiFrame& frame) = 0; uint8_t* frameData(CemiFrame& frame); DeviceObject& _deviceObject; - AddressTableObject& _groupAddressTable; NetworkLayer& _networkLayer; Platform& _platform; #ifdef USE_CEMI_SERVER diff --git a/src/knx/ip_data_link_layer.cpp b/src/knx/ip_data_link_layer.cpp index 13fbdfa..5a9657d 100644 --- a/src/knx/ip_data_link_layer.cpp +++ b/src/knx/ip_data_link_layer.cpp @@ -5,7 +5,6 @@ #include "bits.h" #include "platform.h" #include "device_object.h" -#include "address_table_object.h" #include "knx_ip_routing_indication.h" #include "knx_ip_search_request.h" #include "knx_ip_search_response.h" @@ -18,8 +17,8 @@ #define MIN_LEN_CEMI 10 -IpDataLinkLayer::IpDataLinkLayer(DeviceObject& devObj, AddressTableObject& addrTab, IpParameterObject& ipParam, - NetworkLayer& layer, Platform& platform) : DataLinkLayer(devObj, addrTab, layer, platform), _ipParameters(ipParam) +IpDataLinkLayer::IpDataLinkLayer(DeviceObject& devObj, IpParameterObject& ipParam, + NetworkLayer& layer, Platform& platform) : DataLinkLayer(devObj, layer, platform), _ipParameters(ipParam) { } diff --git a/src/knx/ip_data_link_layer.h b/src/knx/ip_data_link_layer.h index 11c0fca..ee9acbc 100644 --- a/src/knx/ip_data_link_layer.h +++ b/src/knx/ip_data_link_layer.h @@ -11,7 +11,7 @@ class IpDataLinkLayer : public DataLinkLayer using DataLinkLayer::_deviceObject; public: - IpDataLinkLayer(DeviceObject& devObj, AddressTableObject& addrTab, IpParameterObject& ipParam, NetworkLayer& layer, + IpDataLinkLayer(DeviceObject& devObj, IpParameterObject& ipParam, NetworkLayer& layer, Platform& platform); void loop(); @@ -25,4 +25,4 @@ class IpDataLinkLayer : public DataLinkLayer IpParameterObject& _ipParameters; }; -#endif \ No newline at end of file +#endif diff --git a/src/knx/network_layer.cpp b/src/knx/network_layer.cpp index 869c4fc..87ad919 100644 --- a/src/knx/network_layer.cpp +++ b/src/knx/network_layer.cpp @@ -4,7 +4,7 @@ #include "data_link_layer.h" #include "bits.h" -NetworkLayer::NetworkLayer(TransportLayer& layer): _transportLayer(layer) +NetworkLayer::NetworkLayer(DeviceObject &deviceObj, TransportLayer& layer): _transportLayer(layer), _deviceObj(deviceObj) { } @@ -27,6 +27,21 @@ void NetworkLayer::hopCount(uint8_t value) void NetworkLayer::dataIndication(AckType ack, AddressType addrType, uint16_t destination, FrameFormat format, NPDU& npdu, Priority priority, uint16_t source) { HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter; + + // Only for devices which are not a coupler + if (addrType == InduvidualAddress && destination != _deviceObj.induvidualAddress()) + return; + + // TODO: remove. getTSAP() will return 0 later anyway. Get rid of dependency to GAT + //if (addrType == GroupAddress && !_groupAddressTable.contains(destination)) + // return; + +// if (frame.npdu().octetCount() > 0) +// { +// _print("-> DLL "); +// frame.apdu().printPDU(); +// } + if (addrType == InduvidualAddress) { //if (npdu.octetCount() > 0) diff --git a/src/knx/network_layer.h b/src/knx/network_layer.h index 0b1f88e..6a8202e 100644 --- a/src/knx/network_layer.h +++ b/src/knx/network_layer.h @@ -5,11 +5,12 @@ #include "npdu.h" #include "transport_layer.h" class DataLinkLayer; +class DeviceObject; class NetworkLayer { public: - NetworkLayer(TransportLayer& layer); + NetworkLayer(DeviceObject& deviceObj, TransportLayer& layer); void dataLinkLayer(DataLinkLayer& layer); uint8_t hopCount() const; @@ -38,4 +39,6 @@ class NetworkLayer uint8_t _hopCount = 6; DataLinkLayer* _dataLinkLayer = 0; TransportLayer& _transportLayer; + DeviceObject& _deviceObj; + }; diff --git a/src/knx/rf_data_link_layer.cpp b/src/knx/rf_data_link_layer.cpp index 0c06de1..48648a8 100644 --- a/src/knx/rf_data_link_layer.cpp +++ b/src/knx/rf_data_link_layer.cpp @@ -73,9 +73,9 @@ bool RfDataLinkLayer::sendFrame(CemiFrame& frame) return true; } -RfDataLinkLayer::RfDataLinkLayer(DeviceObject& devObj, RfMediumObject& rfMediumObj, AddressTableObject& addrTab, +RfDataLinkLayer::RfDataLinkLayer(DeviceObject& devObj, RfMediumObject& rfMediumObj, NetworkLayer& layer, Platform& platform) - : DataLinkLayer(devObj, addrTab, layer, platform), + : DataLinkLayer(devObj, layer, platform), _rfMediumObj(rfMediumObj), _rfPhy(*this, platform) { @@ -377,4 +377,4 @@ void RfDataLinkLayer::loadNextTxFrame(uint8_t** sendBuffer, uint16_t* sendBuffer delete tx_frame; } -#endif \ No newline at end of file +#endif diff --git a/src/knx/rf_data_link_layer.h b/src/knx/rf_data_link_layer.h index 5508fdd..6232ab5 100644 --- a/src/knx/rf_data_link_layer.h +++ b/src/knx/rf_data_link_layer.h @@ -16,12 +16,11 @@ class RfDataLinkLayer : public DataLinkLayer friend class RfPhysicalLayer; using DataLinkLayer::_deviceObject; - using DataLinkLayer::_groupAddressTable; using DataLinkLayer::_platform; public: - RfDataLinkLayer(DeviceObject& devObj, RfMediumObject& rfMediumObj, AddressTableObject& addrTab, NetworkLayer& layer, - Platform& platform); + RfDataLinkLayer(DeviceObject& devObj, RfMediumObject& rfMediumObj, NetworkLayer& layer, + Platform& platform); void loop(); void enabled(bool value); @@ -60,4 +59,4 @@ class RfDataLinkLayer : public DataLinkLayer uint16_t calcCrcRF(uint8_t* buffer, uint32_t offset, uint32_t len); }; -#endif \ No newline at end of file +#endif diff --git a/src/knx/secure_application_layer.cpp b/src/knx/secure_application_layer.cpp index a5dfbc1..ff4901f 100644 --- a/src/knx/secure_application_layer.cpp +++ b/src/knx/secure_application_layer.cpp @@ -23,18 +23,25 @@ static constexpr uint8_t kSecureDataPdu = 0; static constexpr uint8_t kSecureSyncRequest = 2; static constexpr uint8_t kSecureSyncResponse = 3; -SecureApplicationLayer::SecureApplicationLayer(DeviceObject &deviceObj, SecurityInterfaceObject &secIfObj, AssociationTableObject& assocTable, AddressTableObject &addrTab, BusAccessUnit& bau): - ApplicationLayer(assocTable, bau), +SecureApplicationLayer::SecureApplicationLayer(DeviceObject &deviceObj, SecurityInterfaceObject &secIfObj, BusAccessUnit& bau): + ApplicationLayer(bau), _secIfObj(secIfObj), - _deviceObj(deviceObj), - _addrTab(addrTab) + _deviceObj(deviceObj) { } +void SecureApplicationLayer::groupAddressTable(AddressTableObject &addrTable) +{ + _addrTab = &addrTable; +} + /* from transport layer */ void SecureApplicationLayer::dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) { + if (_addrTab == nullptr) + return; + println("dataGroupIndication"); if (apdu.type() == SecureService) @@ -293,12 +300,15 @@ void SecureApplicationLayer::dataConnectedConfirm(uint16_t tsap) void SecureApplicationLayer::dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl) { + if (_addrTab == nullptr) + return; + println("dataGroupRequest"); if (secCtrl.dataSecurity != DataSecurity::none) { apdu.frame().sourceAddress(_deviceObj.induvidualAddress()); - apdu.frame().destinationAddress(_addrTab.getGroupAddress(tsap)); + apdu.frame().destinationAddress(_addrTab->getGroupAddress(tsap)); apdu.frame().addressType(GroupAddress); uint16_t secureApduLength = apdu.length() + 3 + 6 + 4; // 3(TPCI,APCI,SCF) + sizeof(seqNum) + apdu.length() + 4 @@ -507,7 +517,10 @@ void SecureApplicationLayer::blockCtr0(uint8_t* buffer, uint8_t* seqNum, uint16_ uint16_t SecureApplicationLayer::groupAddressIndex(uint16_t groupAddr) { - return _addrTab.getTsap(groupAddr); + // Just for safety reasons, we should never get here, because the dataGroupIndication will return already return early without doing anything + if (_addrTab == nullptr) + return 0; + return _addrTab->getTsap(groupAddr); } const uint8_t* SecureApplicationLayer::securityKey(uint16_t addr, bool isGroupAddress) diff --git a/src/knx/secure_application_layer.h b/src/knx/secure_application_layer.h index 856904e..0df8f2c 100644 --- a/src/knx/secure_application_layer.h +++ b/src/knx/secure_application_layer.h @@ -28,7 +28,9 @@ class SecureApplicationLayer : public ApplicationLayer * @param assocTable The AssociationTable is used to translate between asap (i.e. group objects) and group addresses. * @param bau methods are called here depending of the content of the APDU */ - SecureApplicationLayer(DeviceObject& deviceObj, SecurityInterfaceObject& secIfObj, AssociationTableObject& assocTable, AddressTableObject& addrTab, BusAccessUnit& bau); + SecureApplicationLayer(DeviceObject& deviceObj, SecurityInterfaceObject& secIfObj, BusAccessUnit& bau); + + void groupAddressTable(AddressTableObject& addrTable); void clearFailureLog(); void getFailureCounters(uint8_t* data); @@ -150,5 +152,5 @@ class SecureApplicationLayer : public ApplicationLayer SecurityInterfaceObject& _secIfObj; DeviceObject& _deviceObj; - AddressTableObject& _addrTab; + AddressTableObject* _addrTab = nullptr; }; diff --git a/src/knx/tpuart_data_link_layer.cpp b/src/knx/tpuart_data_link_layer.cpp index c68db25..6e19d5c 100644 --- a/src/knx/tpuart_data_link_layer.cpp +++ b/src/knx/tpuart_data_link_layer.cpp @@ -265,23 +265,39 @@ void TpUartDataLinkLayer::loop() if (!_isEcho) { uint8_t c = 0x10; - //ceck if individual or group address - if ((buffer[6] & 0x80) == 0) + + // If this is not a nullptr we consider this a device to be a coupler + // TODO: Improve for coupler mode, only ACK according to filter tables + if (_groupAddressTable) { + //check if individual or group address + if ((buffer[6] & 0x80) == 0) + { + //individual + if (_deviceObject.induvidualAddress() == getWord(buffer + 4)) + { + c |= 0x01; + } + } + else + { + //group + if (_groupAddressTable->contains(getWord(buffer + 4)) || getWord(buffer + 4) == 0) + { + c |= 0x01; + } + } + } + else + { + // TODO: test for only our coupler //individual if (_deviceObject.induvidualAddress() == getWord(buffer + 4)) { c |= 0x01; } } - else - { - //group - if (_groupAddressTable.contains(getWord(buffer + 4)) || getWord(buffer + 4) == 0) - { - c |= 0x01; - } - } + _platform.writeUart(c); } } @@ -412,12 +428,17 @@ void TpUartDataLinkLayer::stopChip() #endif } -TpUartDataLinkLayer::TpUartDataLinkLayer(DeviceObject& devObj, AddressTableObject& addrTab, +TpUartDataLinkLayer::TpUartDataLinkLayer(DeviceObject& devObj, NetworkLayer& layer, Platform& platform) - : DataLinkLayer(devObj, addrTab, layer, platform) + : DataLinkLayer(devObj, layer, platform) { } +void TpUartDataLinkLayer::groupAddressTable(AddressTableObject &addrTable) +{ + _groupAddressTable = &addrTable; +} + void TpUartDataLinkLayer::frameBytesReceived(uint8_t* buffer, uint16_t length) { //printHex("=>", buffer, length); @@ -548,4 +569,4 @@ void TpUartDataLinkLayer::loadNextTxFrame() } delete tx_frame; } -#endif \ No newline at end of file +#endif diff --git a/src/knx/tpuart_data_link_layer.h b/src/knx/tpuart_data_link_layer.h index e84bc3e..dd7c593 100644 --- a/src/knx/tpuart_data_link_layer.h +++ b/src/knx/tpuart_data_link_layer.h @@ -11,13 +11,15 @@ class TpUartDataLinkLayer : public DataLinkLayer { using DataLinkLayer::_deviceObject; - using DataLinkLayer::_groupAddressTable; using DataLinkLayer::_platform; public: - TpUartDataLinkLayer(DeviceObject& devObj, AddressTableObject& addrTab, NetworkLayer& layer, + TpUartDataLinkLayer(DeviceObject& devObj, NetworkLayer& layer, Platform& platform); + ; + void groupAddressTable(AddressTableObject& addrTable); + void loop(); void enabled(bool value); bool enabled() const; @@ -62,5 +64,7 @@ class TpUartDataLinkLayer : public DataLinkLayer void dataConBytesReceived(uint8_t* buffer, uint16_t length, bool success); bool resetChip(); void stopChip(); + + AddressTableObject* _groupAddressTable = nullptr; }; -#endif \ No newline at end of file +#endif diff --git a/src/knx/transport_layer.cpp b/src/knx/transport_layer.cpp index 176ff74..1e3a7f8 100644 --- a/src/knx/transport_layer.cpp +++ b/src/knx/transport_layer.cpp @@ -7,8 +7,8 @@ #include "bits.h" #include -TransportLayer::TransportLayer(ApplicationLayer& layer, AddressTableObject& gat): _savedFrame(0), - _savedFrameConnecting(0), _applicationLayer(layer), _groupAddressTable(gat) +TransportLayer::TransportLayer(ApplicationLayer& layer): _savedFrame(0), + _savedFrameConnecting(0), _applicationLayer(layer) { _currentState = Closed; } @@ -18,6 +18,11 @@ void TransportLayer::networkLayer(NetworkLayer& layer) _networkLayer = &layer; } +void TransportLayer::groupAddressTable(AddressTableObject &addrTable) +{ + _groupAddressTable = &addrTable; +} + void TransportLayer::dataIndividualIndication(uint16_t destination, HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu) { //if (tpdu.apdu().length() > 0) @@ -361,7 +366,10 @@ void TransportLayer::dataIndividualConfirm(AckType ack, uint16_t destination, Ho void TransportLayer::dataGroupIndication(uint16_t destination, HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu) { - uint16_t tsap = _groupAddressTable.getTsap(destination); + if (_groupAddressTable == nullptr) + return; + + uint16_t tsap = _groupAddressTable->getTsap(destination); if (tsap == 0) return; @@ -395,7 +403,10 @@ void TransportLayer::dataSystemBroadcastConfirm(AckType ack, HopCountType hopTyp void TransportLayer::dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) { - uint16_t groupAdress = _groupAddressTable.getGroupAddress(tsap); + if (_groupAddressTable == nullptr) + return; + + uint16_t groupAdress = _groupAddressTable->getGroupAddress(tsap); TPDU& tpdu = apdu.frame().tpdu(); _networkLayer->dataGroupRequest(ack, groupAdress, hopType, priority, tpdu); } diff --git a/src/knx/transport_layer.h b/src/knx/transport_layer.h index 76bf861..8814cc3 100644 --- a/src/knx/transport_layer.h +++ b/src/knx/transport_layer.h @@ -16,8 +16,9 @@ enum StateType { Closed, OpenIdle, OpenWait, Connecting }; class TransportLayer { public: - TransportLayer(ApplicationLayer& layer, AddressTableObject& gat); + TransportLayer(ApplicationLayer& layer); void networkLayer(NetworkLayer& layer); + void groupAddressTable(AddressTableObject& addrTable); #pragma region from network layer void dataIndividualIndication(uint16_t destination, HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu); @@ -115,6 +116,6 @@ private: uint8_t _maxRepCount = 3; #pragma endregion ApplicationLayer& _applicationLayer; - AddressTableObject& _groupAddressTable; + AddressTableObject* _groupAddressTable; NetworkLayer* _networkLayer; }; diff --git a/src/knx_facade.h b/src/knx_facade.h index 923195f..6db2ae2 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -27,6 +27,7 @@ #include "linux_platform.h" #include "knx/bau57B0.h" #include "knx/bau27B0.h" + #include "knx/bau091A.h" #endif void buttonUp();