Merge pull request #289 from thelsing/openknx-merge

Openknx merge
This commit is contained in:
thelsing 2024-08-09 21:48:13 +02:00 committed by GitHub
commit 44f90bb949
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
120 changed files with 6003 additions and 1389 deletions

View File

@ -1,10 +0,0 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

View File

@ -1,3 +1,8 @@
cmake_policy(SET CMP0048 NEW)
cmake_minimum_required(VERSION 3.16)
project(knx VERSION 1.5)
add_subdirectory(examples/knx-linux)
add_subdirectory(examples/knx-linux-coupler)
add_subdirectory(examples/knxPython)
add_subdirectory(examples/knxPython)

View File

@ -4,50 +4,7 @@ This projects provides a knx-device stack for arduino (ESP8266, ESP32, SAMD21, R
It implements most of System-B specification and can be configured with ETS.
The necessary knxprod-files can be generated with the [Kaenx-Creator](https://github.com/OpenKNX/Kaenx-Creator) tool.
For ESP8266 and ESP32 [WifiManager](https://github.com/tzapu/WiFiManager) is used to configure wifi.
Don't forget to reset ESP8266 manually (disconnect power) after flashing. The reboot doen't work during configuration with ETS otherwise.
Generated documentation can be found [here](https://knx.readthedocs.io/en/latest/).
## Stack configuration possibilities
Specify prog button GPIO other then `GPIO0`:
```C++
knx.buttonPin(3); // Use GPIO3 Pin
```
Specify a LED GPIO for programming mode other then the `LED_BUILTIN`:
```C++
knx.ledPin(5);
```
Use a custom function instead of a LED connected to GPIO to indicate the programming mode:
```C++
#include <Arduino.h>
#include <Adafruit_NeoPixel.h>
#include <knx.h>
// create a pixel strand with 1 pixel on PIN_NEOPIXEL
Adafruit_NeoPixel pixels(1, PIN_NEOPIXEL);
void progLedOff()
{
pixels.clear();
pixels.show();
}
void progLedOn()
{
pixels.setPixelColor(0, pixels.Color(20, 0, 0));
pixels.show();
}
void main ()
{
knx.setProgLedOffCallback(progLedOff);
knx.setProgLedOnCallback(progLedOn);
[...]
}
```
More configuration options can be found in the examples.
## Usage
See the examples for basic usage options

View File

@ -79,3 +79,20 @@ build_flags =
-DMASK_VERSION=0x07B0
-Wno-unknown-pragmas
-DUSE_DATASECURE
;--- RP2040 -----------------------------------------------
[env:rp2040]
framework = arduino
platform = https://github.com/maxgerhardt/platform-raspberrypi.git#60d6ae8
platform_packages = framework-arduinopico @ https://github.com/earlephilhower/arduino-pico/releases/download/3.9.3/rp2040-3.9.3.zip
board = rpipico
board_build.core = earlephilhower
lib_deps =
knx
build_flags =
-DMASK_VERSION=0x07B0
-DKNX_FLASH_SIZE=4096
-D PIO_FRAMEWORK_ARDUINO_ENABLE_RTTI
-Wno-unknown-pragmas

View File

@ -146,3 +146,26 @@ build_flags =
-Wno-unknown-pragmas
extra_scripts = ../scripts/stm32rdu.py
;--- RP2040 -----------------------------------------------
[env:rp2040]
framework = arduino
platform = https://github.com/maxgerhardt/platform-raspberrypi.git#60d6ae8
platform_packages = framework-arduinopico @ https://github.com/earlephilhower/arduino-pico/releases/download/3.9.3/rp2040-3.9.3.zip
board = rpipico
board_build.core = earlephilhower
; We consider that the this projects is opened within its project directory
; while working with VS Code.
lib_deps =
knx=file://../../../knx
lib_ldf_mode = deep+
build_flags =
-DMASK_VERSION=0x07B0
-DKNX_FLASH_SIZE=4096
-D PIO_FRAMEWORK_ARDUINO_ENABLE_RTTI
-Wno-unknown-pragmas

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 2.7)
project(knx-linux-coupler)
cmake_minimum_required(VERSION 3.16)
project(knx-linux-coupler VERSION 1.5)
set(LIBRARIES_FROM_REFERENCES "")
set(SOURCES

View File

@ -62,8 +62,6 @@ int FdskCalculator::toBase32(uint8_t* in, long length, uint8_t*& out, bool usePa
char standardPaddingChar = '=';
int result = 0;
int count = 0;
int bufSize = 8;
int index = 0;
int size = 0; // size of temporary array
uint8_t* temp = nullptr;

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 2.7)
project(knx-linux)
cmake_minimum_required(VERSION 3.16)
project(knx-linux VERSION 1.5)
set(LIBRARIES_FROM_REFERENCES "")
set(SOURCES

View File

@ -62,8 +62,6 @@ int FdskCalculator::toBase32(uint8_t* in, long length, uint8_t*& out, bool usePa
char standardPaddingChar = '=';
int result = 0;
int count = 0;
int bufSize = 8;
int index = 0;
int size = 0; // size of temporary array
uint8_t* temp = nullptr;

View File

@ -14,8 +14,9 @@ framework = arduino
; VID must be changed to some known KNX Manufacturer
; so that the KNX USB interface gets recognized by ETS
;extra_scripts = pre:custom_hwids.py
;board_build.usb_product="KNX RF - USB Interface"
; not possible within ci
;;extra_scripts = pre:custom_hwids.py
;;board_build.usb_product="KNX RF - USB Interface"
lib_deps =
SPI

View File

@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 2.7)
project(knx)
cmake_minimum_required(VERSION 3.16)
project(knx VERSION 1.5)
add_subdirectory(pybind11)
@ -139,4 +139,4 @@ include_directories(../../src)
#set_target_properties(knx PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${outdir})
set_target_properties(knx PROPERTIES OUTPUT_NAME knx)
set_property(TARGET knx PROPERTY CXX_STANDARD 11)
target_compile_definitions(knx PUBLIC -DMASK_VERSION=0x57B0)
target_compile_definitions(knx PUBLIC -DMASK_VERSION=0x57B0)

16
library.json Normal file
View File

@ -0,0 +1,16 @@
{
"name": "knx",
"version": "1.5.0",
"dependencies": {
},
"description": "knx stack",
"authors": [
{
"name": "Thomas Kunze"
}
],
"repository": {
"type": "git",
"url": "https://github.com/thelsing/knx"
}
}

View File

@ -1,10 +1,10 @@
name=knx
version=1.2.0
author=Thomas Kunze
version=1.5.0
author=Thomas Kunze, et. al.
maintainer=Thomas Kunze
sentence=knx stack
paragraph=
category=Communication
url=https://github.com/thelsing/knx
architectures=*
includes=knx.h
includes=knx.h

View File

@ -103,6 +103,11 @@ size_t ArduinoPlatform::readBytesUart(uint8_t *buffer, size_t length)
return length;
}
void ArduinoPlatform::flushUart()
{
return _knxSerial->flush();
}
#ifndef KNX_NO_SPI
void ArduinoPlatform::setupSpi()
{

View File

@ -25,6 +25,7 @@ class ArduinoPlatform : public Platform
virtual size_t writeUart(const uint8_t* buffer, size_t size);
virtual int readUart();
virtual size_t readBytesUart(uint8_t* buffer, size_t length);
virtual void flushUart();
//spi
#ifndef KNX_NO_SPI

View File

@ -99,32 +99,50 @@ bool Esp32Platform::sendBytesMultiCast(uint8_t * buffer, uint16_t len)
return true;
}
int Esp32Platform::readBytesMultiCast(uint8_t * buffer, uint16_t maxLen)
int Esp32Platform::readBytesMultiCast(uint8_t * buffer, uint16_t maxLen, uint32_t& src_addr, uint16_t& src_port)
{
int len = _udp.parsePacket();
if (len == 0)
return 0;
if (len > maxLen)
{
KNX_DEBUG_SERIAL.printf("udp buffer to small. was %d, needed %d\n", maxLen, len);
fatalError();
println("Unexpected UDP data packet length - drop packet");
for (size_t i = 0; i < len; i++)
_udp.read();
return 0;
}
_udp.read(buffer, len);
//printHex("-> ", buffer, len);
_remoteIP = _udp.remoteIP();
_remotePort = _udp.remotePort();
src_addr = htonl(_remoteIP);
src_port = _remotePort;
// print("Remote IP: ");
// print(_udp.remoteIP().toString().c_str());
// printHex("-> ", buffer, len);
return len;
}
bool Esp32Platform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len)
{
IPAddress ucastaddr(htonl(addr));
println("sendBytesUniCast endPacket fail");
if(_udp.beginPacket(ucastaddr, port) == 1) {
if(!addr)
ucastaddr = _remoteIP;
if(!port)
port = _remotePort;
if(_udp.beginPacket(ucastaddr, port) == 1)
{
_udp.write(buffer, len);
if(_udp.endPacket() == 0) println("sendBytesUniCast endPacket fail");
}
else println("sendBytesUniCast beginPacket fail");
else
println("sendBytesUniCast beginPacket fail");
return true;
}

View File

@ -30,7 +30,7 @@ public:
void setupMultiCast(uint32_t addr, uint16_t port) override;
void closeMultiCast() override;
bool sendBytesMultiCast(uint8_t* buffer, uint16_t len) override;
int readBytesMultiCast(uint8_t* buffer, uint16_t maxLen) override;
int readBytesMultiCast(uint8_t* buffer, uint16_t maxLen, uint32_t& src_addr, uint16_t& src_port) override;
//unicast
bool sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) override;
@ -38,6 +38,10 @@ public:
//memory
uint8_t* getEepromBuffer(uint32_t size);
void commitToEeprom();
protected: IPAddress _remoteIP;
protected: uint16_t _remotePort;
private:
WiFiUDP _udp;
int8_t _rxPin = -1;

View File

@ -722,6 +722,38 @@ void ApplicationLayer::propertyDescriptionReadResponse(AckType ack, Priority pri
individualSend(ack, hopType, priority, asap, apdu, secCtrl);
}
void ApplicationLayer::propertyExtDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint16_t objectType, uint16_t objectInstance, uint16_t propertyId, uint16_t propertyIndex, uint8_t descriptionType, bool writeEnable, uint8_t type,
uint16_t maxNumberOfElements, uint8_t access)
{
CemiFrame frame(16);
APDU& apdu = frame.apdu();
apdu.type(PropertyExtDescriptionResponse);
uint8_t* data = apdu.data();
data[1] = (objectType & 0xff00) >> 8;
data[2] = (objectType & 0x00ff);
data[3] = (objectInstance & 0x0ff0) >> 4;
data[4] = (objectInstance & 0x000f) << 4 | (propertyId & 0x0f00) >> 8;
data[5] = (propertyId & 0x00ff);
data[6] = (descriptionType & 0x000f) << 4 | (propertyIndex & 0x0f00) >> 8;
data[7] = (propertyIndex & 0x00ff);
data[8] = 0; // DataPointType ??
data[9] = 0; // DataPointType ??
data[10] = 0; // DataPointType ??
data[11] = 0; // DataPointType ??
if (writeEnable)
data[12] |= 0x80;
data[12] |= (type & 0x3f);
pushWord(maxNumberOfElements & 0xfff, data + 13);
data[15] = access;
individualSend(ack, hopType, priority, asap, apdu, secCtrl);
}
void ApplicationLayer::memoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress)
{
@ -740,6 +772,18 @@ void ApplicationLayer::memoryReadResponse(AckType ack, Priority priority, HopCou
memorySend(MemoryResponse, ack, priority, hopType, asap, secCtrl, number, memoryAddress, memoryData);
}
void ApplicationLayer::memoryRouterReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t * memoryData)
{
memoryRouterSend(MemoryRouterReadResponse, ack, priority, hopType, asap, secCtrl, number, memoryAddress, memoryData);
}
void ApplicationLayer::memoryRoutingTableReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t * memoryData)
{
memoryRoutingTableSend(RoutingTableReadResponse, ack, priority, hopType, asap, secCtrl, number, memoryAddress, memoryData);
}
void ApplicationLayer::memoryExtReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ReturnCodes code,
uint8_t number, uint32_t memoryAddress, uint8_t * memoryData)
{
@ -962,6 +1006,34 @@ void ApplicationLayer::memorySend(ApduType type, AckType ack, Priority priority,
individualSend(ack, hopType, priority, asap, apdu, secCtrl);
}
void ApplicationLayer::memoryRouterSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t * memoryData)
{
CemiFrame frame(4 + number);
APDU& apdu = frame.apdu();
apdu.type(type);
uint8_t* data = apdu.data();
data[1] |= (number & 0xf);
pushWord(memoryAddress & 0xffff, data + 2);
if (number > 0)
memcpy(data + 4, memoryData, number);
individualSend(ack, hopType, priority, asap, apdu, secCtrl);
}
void ApplicationLayer::memoryRoutingTableSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t * memoryData)
{
CemiFrame frame(4 + number);
APDU& apdu = frame.apdu();
apdu.type(type);
uint8_t* data = apdu.data();
data[1] |= (number & 0xf);
pushWord(memoryAddress & 0xffff, data + 2);
if (number > 0)
memcpy(data + 4, memoryData, number);
individualSend(ack, hopType, priority, asap, apdu, secCtrl);
}
void ApplicationLayer::userMemorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint32_t memoryAddress, uint8_t * memoryData)
{
@ -1084,6 +1156,17 @@ void ApplicationLayer::individualIndication(HopCountType hopType, Priority prior
case PropertyDescriptionRead:
_bau.propertyDescriptionReadIndication(priority, hopType, tsap, secCtrl, data[1], data[2], data[3]);
break;
case PropertyExtDescriptionRead:
{
ObjectType objectType = (ObjectType)(((data[1] & 0xff) << 8) | (data[2] & 0xff));
uint16_t objectInstance = ((data[3] & 0xff) << 4) | ((data[4] & 0xf0) >> 4);
uint16_t propertyId = ((data[4] & 0x0f) << 8) | (data[5] & 0xff);
uint8_t descriptionType = (data[6] & 0xf0) >> 4;
uint16_t propertyIndex = ((data[7] & 0x0f) << 8) | (data[8] & 0xff);
_bau.propertyExtDescriptionReadIndication(priority, hopType, tsap, secCtrl, objectType, objectInstance, propertyId, descriptionType, propertyIndex);
break;
}
case PropertyDescriptionResponse:
_bau.propertyDescriptionReadAppLayerConfirm(priority, hopType, tsap, secCtrl, data[1], data[2], data[3],
(data[4] & 0x80) > 0, data[4] & 0x3f, getWord(data + 5) & 0xfff, data[7]);
@ -1097,8 +1180,30 @@ void ApplicationLayer::individualIndication(HopCountType hopType, Priority prior
case MemoryWrite:
_bau.memoryWriteIndication(priority, hopType, tsap, secCtrl, data[0] & 0x3f, getWord(data + 1), data + 3);
break;
case MemoryExtRead:
{
// EC
case MemoryRouterWrite:
print("MemoryRouterWrite: ");
_bau.memoryRouterWriteIndication(priority, hopType, tsap, secCtrl, data[1], getWord(data + 2), data + 4);
break;
case MemoryRouterReadResponse:
_bau.memoryRouterReadAppLayerConfirm(priority, hopType, tsap, secCtrl, data[1], getWord(data + 2), data + 4);
break;
case RoutingTableOpen:
println("Received OpenRoutingTable APDU, doing nothing");
break;
case RoutingTableRead:
_bau.memoryRoutingTableReadIndication(priority, hopType, tsap, secCtrl, data[1], getWord(data + 2));
break;
case RoutingTableReadResponse:
_bau.memoryRoutingTableReadAppLayerConfirm(priority, hopType, tsap, secCtrl, data[1], getWord(data + 2), data + 4);
break;
case RoutingTableWrite:
_bau.memoryRoutingTableWriteIndication(priority, hopType, tsap, secCtrl, data[1], getWord(data + 2), data + 4);
break;
// end EC
case MemoryExtRead: {
uint8_t number = data[1];
uint32_t memoryAddress = ((data[2] & 0xff) << 16) | ((data[3] & 0xff) << 8) | (data[4] & 0xff);
_bau.memoryExtReadIndication(priority, hopType, tsap, secCtrl, number, memoryAddress);
@ -1162,7 +1267,7 @@ void ApplicationLayer::individualIndication(HopCountType hopType, Priority prior
}
default:
print("Individual-indication: unhandled APDU-Type: ");
println(apdu.type());
apdu.printPDU();
}
}
@ -1210,6 +1315,9 @@ void ApplicationLayer::individualConfirm(AckType ack, HopCountType hopType, Prio
case PropertyDescriptionRead:
_bau.propertyDescriptionReadLocalConfirm(ack, priority, hopType, tsap, secCtrl, data[1], data[2], data[3], status);
break;
case PropertyExtDescriptionRead:
_bau.propertyExtDescriptionReadLocalConfirm(ack, priority, hopType, tsap, secCtrl, data[1], data[2], data[3], status);
break;
case PropertyDescriptionResponse:
_bau.propertyDescriptionReadResponseConfirm(ack, priority, hopType, tsap, secCtrl, data[1], data[2], data[3],
(data[4] & 0x80) > 0, data[4] & 0x3f, getWord(data + 5) & 0xfff, data[7], status);

View File

@ -125,10 +125,17 @@ class ApplicationLayer
void propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type,
uint16_t maxNumberOfElements, uint8_t access);
void propertyExtDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint16_t objectType, uint16_t objectInstance, uint16_t propertyId, uint16_t propertyIndex, uint8_t descriptionType, bool writeEnable, uint8_t type,
uint16_t maxNumberOfElements, uint8_t access);
void memoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress);
void memoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t* data);
void memoryRouterReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t* data);
void memoryRoutingTableReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t* data);
void memoryExtReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ReturnCodes code, uint8_t number,
uint32_t memoryAddress, uint8_t* data);
void memoryExtWriteResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ReturnCodes code, uint8_t number,
@ -195,6 +202,12 @@ class ApplicationLayer
uint16_t objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length);
void memorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t* memoryData);
// Added EC
void memoryRouterSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t* memoryData);
void memoryRoutingTableSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t* memoryData);
//
void userMemorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint8_t number, uint32_t memoryAddress, uint8_t* memoryData);
void groupValueSend(ApduType type, AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* data, uint8_t& dataLength);

View File

@ -6,7 +6,11 @@
#include <cstring>
ApplicationProgramObject::ApplicationProgramObject(Memory& memory)
#if MASK_VERSION == 0x091A
: TableObject(memory, 0x0100, 0x0100)
#else
: TableObject(memory)
#endif
{
Property* properties[] =
{

View File

@ -151,10 +151,19 @@ void BusAccessUnit::propertyDescriptionReadLocalConfirm(AckType ack, Priority pr
{
}
void BusAccessUnit::propertyExtDescriptionReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint16_t objectIndex, uint8_t propertyId, uint16_t propertyIndex, bool status)
{
}
void BusAccessUnit::propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex)
{
}
void BusAccessUnit::propertyExtDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl,
uint16_t objectType, uint16_t objectInstance, uint16_t propertyId, uint8_t descriptionType, uint16_t propertyIndex)
{
}
void BusAccessUnit::propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, uint16_t maxNumberOfElements, uint8_t access)
{
}
@ -191,6 +200,22 @@ void BusAccessUnit::memoryWriteIndication(Priority priority, HopCountType hopTyp
{
}
void BusAccessUnit::memoryRouterWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data)
{
}
void BusAccessUnit::memoryRouterReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data)
{
}
void BusAccessUnit::memoryRoutingTableReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress)
{
}
void BusAccessUnit::memoryRoutingTableReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data)
{
}
void BusAccessUnit::memoryRoutingTableWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t *data)
{
}
void BusAccessUnit::memoryExtReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress, bool status)
{
}

View File

@ -73,8 +73,12 @@ class BusAccessUnit
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool confirmed);
virtual void propertyDescriptionReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl,
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool status);
virtual void propertyExtDescriptionReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl,
uint16_t objectIndex, uint8_t propertyId, uint16_t propertyIndex, bool status);
virtual void propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl,
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex);
virtual void propertyExtDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl,
uint16_t objectType, uint16_t objectInstance, uint16_t propertyId, uint8_t descriptionType, uint16_t propertyIndex);
virtual void propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl,
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type,
uint16_t maxNumberOfElements, uint8_t access);
@ -95,6 +99,13 @@ class BusAccessUnit
uint16_t memoryAddress, uint8_t* data, bool status);
virtual void memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t* data);
virtual void memoryRouterWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t* data);
virtual void memoryRouterReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t* data);
virtual void memoryRoutingTableReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress);
virtual void memoryRoutingTableReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t *data);
virtual void memoryRoutingTableWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t *data);
virtual void memoryExtReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint32_t memoryAddress, bool status);
virtual void memoryExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint32_t memoryAddress);

View File

@ -9,9 +9,8 @@
using namespace std;
Bau07B0::Bau07B0(Platform& platform)
: BauSystemBDevice(platform),
_dlLayer(_deviceObj, _netLayer.getInterface(), _platform, (ITpUartCallBacks&) *this, (DataLinkLayerCallbacks*) this),
DataLinkLayerCallbacks()
: BauSystemBDevice(platform), DataLinkLayerCallbacks(),
_dlLayer(_deviceObj, _netLayer.getInterface(), _platform, *this, (ITpUartCallBacks&) *this, (DataLinkLayerCallbacks*) this)
#ifdef USE_CEMI_SERVER
, _cemiServer(*this)
#endif
@ -78,7 +77,7 @@ InterfaceObject* Bau07B0::getInterfaceObject(uint8_t idx)
}
}
InterfaceObject* Bau07B0::getInterfaceObject(ObjectType objectType, uint8_t objectInstance)
InterfaceObject* Bau07B0::getInterfaceObject(ObjectType objectType, uint16_t objectInstance)
{
// We do not use it right now.
// Required for coupler mode as there are multiple router objects for example
@ -128,27 +127,33 @@ void Bau07B0::loop()
#endif
}
bool Bau07B0::isAckRequired(uint16_t address, bool isGrpAddr)
TPAckType Bau07B0::isAckRequired(uint16_t address, bool isGrpAddr)
{
if (isGrpAddr)
{
// ACK for broadcasts
if (address == 0)
return true;
return TPAckType::AckReqAck;
// is group address in group address table? ACK if yes.
return _addrTable.contains(address);
if(_addrTable.contains(address))
return TPAckType::AckReqAck;
else
return TPAckType::AckReqNone;
}
// Also ACK for our own individual address
if (address == _deviceObj.individualAddress())
return true;
return TPAckType::AckReqAck;
if (address == 0)
{
println("Invalid broadcast detected: destination address is 0, but address type is \"individual\"");
}
return false;
return TPAckType::AckReqNone;
}
TpUartDataLinkLayer* Bau07B0::getDataLinkLayer() {
return (TpUartDataLinkLayer*)&_dlLayer;
}
#endif

View File

@ -15,13 +15,14 @@ class Bau07B0 : public BauSystemBDevice, public ITpUartCallBacks, public DataLin
void loop() override;
bool enabled() override;
void enabled(bool value) override;
TpUartDataLinkLayer* getDataLinkLayer();
protected:
InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint16_t objectInstance);
// For TP1 only
bool isAckRequired(uint16_t address, bool isGrpAddr) override;
TPAckType isAckRequired(uint16_t address, bool isGrpAddr) override;
private:
TpUartDataLinkLayer _dlLayer;

View File

@ -8,16 +8,19 @@
using namespace std;
/* ToDos
Announce the line status of sec side 03_05_01 4.4.3
implement PID_COUPLER_SERVICES_CONTROL 03_05_01 4.4.7
*/
Bau091A::Bau091A(Platform& platform)
: BauSystemBCoupler(platform),
_routerObj(memory()),
: BauSystemBCoupler(platform), DataLinkLayerCallbacks(),
_routerObj(memory(), 0x200, 0x2000), // the Filtertable of 0x091A IP Routers is fixed at 0x200 and 0x2000 long
_ipParameters(_deviceObj, platform),
_dlLayerPrimary(_deviceObj, _ipParameters, _netLayer.getPrimaryInterface(), _platform, (DataLinkLayerCallbacks*) this),
_dlLayerSecondary(_deviceObj, _netLayer.getSecondaryInterface(), platform, (ITpUartCallBacks&) *this, (DataLinkLayerCallbacks*) this),
DataLinkLayerCallbacks()
_dlLayerPrimary(_deviceObj, _ipParameters, _netLayer.getPrimaryInterface(), _platform, *this, (DataLinkLayerCallbacks*) this),
_dlLayerSecondary(_deviceObj, _netLayer.getSecondaryInterface(), platform, *this, (ITpUartCallBacks&) *this, (DataLinkLayerCallbacks*) this)
#ifdef USE_CEMI_SERVER
,
_cemiServer(*this)
, _cemiServer(*this)
#endif
{
// Before accessing anything of the router object they have to be initialized according to the used medium
@ -33,9 +36,14 @@ Bau091A::Bau091A(Platform& platform)
#ifdef USE_CEMI_SERVER
_cemiServerObject.setMediumTypeAsSupported(DptMedium::KNX_IP);
_cemiServerObject.setMediumTypeAsSupported(DptMedium::KNX_TP1);
_cemiServer.dataLinkLayerPrimary(_dlLayerPrimary);
_cemiServer.dataLinkLayer(_dlLayerSecondary); // Secondary I/F is the important one!
_dlLayerPrimary.cemiServer(_cemiServer);
_dlLayerSecondary.cemiServer(_cemiServer);
_memory.addSaveRestore(&_cemiServerObject);
uint8_t count = 1;
uint16_t suppCommModes = 0x0100;
_cemiServerObject.writeProperty(PID_COMM_MODES_SUPPORTED, 1, (uint8_t*)&suppCommModes, count); // set the properties Bit 0 to 1 meaning "LinkLayer supported"
#endif
_memory.addSaveRestore(&_routerObj);
@ -92,7 +100,7 @@ InterfaceObject* Bau091A::getInterfaceObject(uint8_t idx)
}
}
InterfaceObject* Bau091A::getInterfaceObject(ObjectType objectType, uint8_t objectInstance)
InterfaceObject* Bau091A::getInterfaceObject(ObjectType objectType, uint16_t objectInstance)
{
// We do not use it right now.
// Required for coupler mode as there are multiple router objects for example
@ -139,6 +147,9 @@ void Bau091A::enabled(bool value)
{
_dlLayerPrimary.enabled(value);
_dlLayerSecondary.enabled(value);
// ToDo change frame repitition in the TP layer - but default is ok.
//_dlLayerSecondary.setFrameRepetition(3,3);
}
void Bau091A::loop()
@ -148,23 +159,77 @@ void Bau091A::loop()
BauSystemBCoupler::loop();
}
bool Bau091A::isAckRequired(uint16_t address, bool isGrpAddr)
TPAckType Bau091A::isAckRequired(uint16_t address, bool isGrpAddr)
{
//only called from TpUartDataLinkLayer
TPAckType ack = TPAckType::AckReqNone;
uint8_t lcconfig = LCCONFIG::PHYS_FRAME_ROUT | LCCONFIG::PHYS_REPEAT | LCCONFIG::BROADCAST_REPEAT | LCCONFIG::GROUP_IACK_ROUT | LCCONFIG::PHYS_IACK_NORMAL; // default value from spec. in case prop is not availible.
Property* prop_lcconfig = _routerObj.property(PID_SUB_LCCONFIG);
if(lcconfig)
prop_lcconfig->read(lcconfig);
if (isGrpAddr)
{
// ACK for broadcasts
if (address == 0)
return true;
ack = TPAckType::AckReqAck;
// is group address in filter table? ACK if yes.
return _routerObj.isGroupAddressInFilterTable(address);
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
if(_dlLayerPrimary.isSentToTunnel(address, isGrpAddr))
ack = TPAckType::AckReqAck;
#endif
}
else
{
return _netLayer.isRoutedIndividualAddress(address);
if((lcconfig & LCCONFIG::PHYS_IACK) == LCCONFIG::PHYS_IACK_ALL)
ack = TPAckType::AckReqAck;
else if((lcconfig & LCCONFIG::PHYS_IACK) == LCCONFIG::PHYS_IACK_NACK)
ack = TPAckType::AckReqNack;
else
if(_netLayer.isRoutedIndividualAddress(address, 1) || address == _deviceObj.individualAddress()) // Also ACK for our own individual address
ack = TPAckType::AckReqAck;
else
ack = TPAckType::AckReqNone;
#ifdef KNX_TUNNELING
if(_dlLayerPrimary.isSentToTunnel(address, isGrpAddr))
ack = TPAckType::AckReqAck;
#endif
}
return false;
return ack;
}
bool Bau091A::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 = _routerObj.loadState() == LS_LOADED;
#ifdef USE_DATASECURE
_configured &= _secIfObj.loadState() == LS_LOADED;
#endif
return _configured;
}
IpDataLinkLayer* Bau091A::getPrimaryDataLinkLayer() {
return (IpDataLinkLayer*)&_dlLayerPrimary;
}
TpUartDataLinkLayer* Bau091A::getSecondaryDataLinkLayer() {
return (TpUartDataLinkLayer*)&_dlLayerSecondary;
}
#endif

View File

@ -17,13 +17,16 @@ class Bau091A : public BauSystemBCoupler, public ITpUartCallBacks, public DataLi
void loop() override;
bool enabled() override;
void enabled(bool value) override;
bool configured() override;
IpDataLinkLayer* getPrimaryDataLinkLayer();
TpUartDataLinkLayer* getSecondaryDataLinkLayer();
protected:
InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint16_t objectInstance);
// For TP1 only
bool isAckRequired(uint16_t address, bool isGrpAddr) override;
TPAckType isAckRequired(uint16_t address, bool isGrpAddr) override;
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
private:

View File

@ -10,7 +10,7 @@ using namespace std;
Bau27B0::Bau27B0(Platform& platform)
: BauSystemBDevice(platform),
_dlLayer(_deviceObj, _rfMediumObj, _netLayer.getInterface(), _platform)
_dlLayer(_deviceObj, _rfMediumObj, _netLayer.getInterface(), _platform, *this)
#ifdef USE_CEMI_SERVER
, _cemiServer(*this)
#endif
@ -90,7 +90,7 @@ InterfaceObject* Bau27B0::getInterfaceObject(uint8_t idx)
}
}
InterfaceObject* Bau27B0::getInterfaceObject(ObjectType objectType, uint8_t objectInstance)
InterfaceObject* Bau27B0::getInterfaceObject(ObjectType objectType, uint16_t objectInstance)
{
// We do not use it right now.
// Required for coupler mode as there are multiple router objects for example
@ -181,5 +181,7 @@ void Bau27B0::domainAddressSerialNumberReadLocalConfirm(Priority priority, HopCo
{
}
RfDataLinkLayer* Bau27B0::getDataLinkLayer() {
return (RfDataLinkLayer*)&_dlLayer;
}
#endif // #ifdef USE_RF

View File

@ -22,9 +22,10 @@ class Bau27B0 : public BauSystemBDevice
bool enabled() override;
void enabled(bool value) override;
RfDataLinkLayer* getDataLinkLayer();
protected:
InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint16_t objectInstance);
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
private:

View File

@ -14,8 +14,8 @@ Bau2920::Bau2920(Platform& platform)
_rtObjPrimary(memory()),
_rtObjSecondary(memory()),
_rfMediumObject(),
_dlLayerPrimary(_deviceObj, _netLayer.getPrimaryInterface(), _platform, (ITpUartCallBacks&) *this),
_dlLayerSecondary(_deviceObj, _rfMediumObject, _netLayer.getSecondaryInterface(), platform)
_dlLayerPrimary(_deviceObj, _netLayer.getPrimaryInterface(), _platform, *this, (ITpUartCallBacks&) *this),
_dlLayerSecondary(_deviceObj, _rfMediumObject, _netLayer.getSecondaryInterface(), platform, *this)
#ifdef USE_CEMI_SERVER
,
_cemiServer(*this)
@ -97,7 +97,7 @@ InterfaceObject* Bau2920::getInterfaceObject(uint8_t idx)
}
}
InterfaceObject* Bau2920::getInterfaceObject(ObjectType objectType, uint8_t objectInstance)
InterfaceObject* Bau2920::getInterfaceObject(ObjectType objectType, uint16_t objectInstance)
{
// We do not use it right now.
// Required for coupler mode as there are multiple router objects for example
@ -154,4 +154,11 @@ void Bau2920::loop()
BauSystemBCoupler::loop();
}
TpUartDataLinkLayer* Bau2920::getPrimaryDataLinkLayer() {
return (TpUartDataLinkLayer*)&_dlLayerPrimary;
}
RfDataLinkLayer* Bau2920::getSecondaryDataLinkLayer() {
return (RfDataLinkLayer*)&_dlLayerSecondary;
}
#endif

View File

@ -22,9 +22,11 @@ class Bau2920 : public BauSystemBCoupler
bool enabled() override;
void enabled(bool value) override;
TpUartDataLinkLayer* getPrimaryDataLinkLayer();
RfDataLinkLayer* getSecondaryDataLinkLayer();
protected:
InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint16_t objectInstance);
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
private:

View File

@ -11,7 +11,7 @@ using namespace std;
Bau57B0::Bau57B0(Platform& platform)
: BauSystemBDevice(platform), DataLinkLayerCallbacks(),
_ipParameters(_deviceObj, platform),
_dlLayer(_deviceObj, _ipParameters, _netLayer.getInterface(), _platform, (DataLinkLayerCallbacks*) this)
_dlLayer(_deviceObj, _ipParameters, _netLayer.getInterface(), _platform, *this, (DataLinkLayerCallbacks*) this)
#ifdef USE_CEMI_SERVER
, _cemiServer(*this)
#endif
@ -83,7 +83,7 @@ InterfaceObject* Bau57B0::getInterfaceObject(uint8_t idx)
}
}
InterfaceObject* Bau57B0::getInterfaceObject(ObjectType objectType, uint8_t objectInstance)
InterfaceObject* Bau57B0::getInterfaceObject(ObjectType objectType, uint16_t objectInstance)
{
// We do not use it right now.
// Required for coupler mode as there are multiple router objects for example
@ -143,4 +143,7 @@ void Bau57B0::loop()
#endif
}
IpDataLinkLayer* Bau57B0::getDataLinkLayer() {
return (IpDataLinkLayer*)&_dlLayer;
}
#endif

View File

@ -15,10 +15,11 @@ class Bau57B0 : public BauSystemBDevice, public DataLinkLayerCallbacks
void loop() override;
bool enabled() override;
void enabled(bool value) override;
IpDataLinkLayer* getDataLinkLayer();
protected:
InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint16_t objectInstance);
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
private:

View File

@ -112,6 +112,50 @@ void BauSystemB::deviceDescriptorReadIndication(Priority priority, HopCountType
pushWord(_deviceObj.maskVersion(), data);
applicationLayer().deviceDescriptorReadResponse(AckRequested, priority, hopType, asap, secCtrl, descriptorType, data);
}
void BauSystemB::memoryRouterWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t *data)
{
print("Writing memory at: ");
print(memoryAddress, HEX);
print(" length: ");
print(number);
print(" data: ");
printHex("=>", data, number);
_memory.writeMemory(memoryAddress, number, data);
if (_deviceObj.verifyMode())
{
print("Sending Read indication");
memoryRouterReadIndication(priority, hopType, asap, secCtrl, number, memoryAddress, data);
}
}
void BauSystemB::memoryRouterReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t *data)
{
applicationLayer().memoryRouterReadResponse(AckRequested, priority, hopType, asap, secCtrl, number, memoryAddress, data);
}
void BauSystemB::memoryRoutingTableReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t *data)
{
applicationLayer().memoryRoutingTableReadResponse(AckRequested, priority, hopType, asap, secCtrl, number, memoryAddress, data);
}
void BauSystemB::memoryRoutingTableReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress)
{
memoryRoutingTableReadIndication(priority, hopType, asap, secCtrl, number, memoryAddress, _memory.toAbsolute(memoryAddress));
}
void BauSystemB::memoryRoutingTableWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t *data)
{
print("Writing memory at: ");
print(memoryAddress, HEX);
print(" length: ");
print(number);
print(" data: ");
printHex("=>", data, number);
_memory.writeMemory(memoryAddress, number, data);
if (_deviceObj.verifyMode())
memoryRoutingTableReadIndication(priority, hopType, asap, secCtrl, number, memoryAddress, data);
}
void BauSystemB::memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t * data)
@ -215,6 +259,33 @@ void BauSystemB::propertyDescriptionReadIndication(Priority priority, HopCountTy
writeEnable, type, numberOfElements, access);
}
void BauSystemB::propertyExtDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl,
uint16_t objectType, uint16_t objectInstance, uint16_t propertyId, uint8_t descriptionType, uint16_t propertyIndex)
{
uint8_t pid = propertyId;
uint8_t pidx = propertyIndex;
if(propertyId > 0xFF || propertyIndex > 0xFF)
{
println("BauSystemB::propertyExtDescriptionReadIndication: propertyId or Idx > 256 are not supported");
return;
}
if(descriptionType != 0)
{
println("BauSystemB::propertyExtDescriptionReadIndication: only descriptionType 0 supported");
return;
}
bool writeEnable = false;
uint8_t type = 0;
uint16_t numberOfElements = 0;
uint8_t access = 0;
InterfaceObject* obj = getInterfaceObject((ObjectType)objectType, objectInstance);
if (obj)
obj->readPropertyDescription(pid, pidx, writeEnable, type, numberOfElements, access);
applicationLayer().propertyExtDescriptionReadResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, propertyIndex,
descriptionType, writeEnable, type, numberOfElements, access);
}
void BauSystemB::propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length)
{
@ -246,6 +317,17 @@ void BauSystemB::propertyValueReadIndication(Priority priority, HopCountType hop
{
uint8_t size = 0;
uint8_t elementCount = numberOfElements;
#ifdef LOG_KNX_PROP
print("propertyValueReadIndication: ObjIdx ");
print(objectIndex);
print(" propId ");
print(propertyId);
print(" num ");
print(numberOfElements);
print(" start ");
print(startIndex);
#endif
InterfaceObject* obj = getInterfaceObject(objectIndex);
if (obj)
{

View File

@ -51,7 +51,7 @@ class BauSystemB : protected BusAccessUnit
protected:
virtual ApplicationLayer& applicationLayer() = 0;
virtual InterfaceObject* getInterfaceObject(uint8_t idx) = 0;
virtual InterfaceObject* getInterfaceObject(ObjectType objectType, uint8_t objectInstance) = 0;
virtual InterfaceObject* getInterfaceObject(ObjectType objectType, uint16_t objectInstance) = 0;
void memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t* data) override;
@ -59,6 +59,15 @@ class BauSystemB : protected BusAccessUnit
uint16_t memoryAddress) override;
void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t * data);
void memoryRouterWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t *data);
void memoryRouterReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t *data);
void memoryRoutingTableWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t *data);
void memoryRoutingTableReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t *data);
void memoryRoutingTableReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number, uint16_t memoryAddress);
//
void memoryExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
uint32_t memoryAddress, uint8_t* data) override;
void memoryExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t number,
@ -71,6 +80,8 @@ class BauSystemB : protected BusAccessUnit
uint32_t memoryAddress, uint8_t* memoryData) override;
void propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex,
uint8_t propertyId, uint8_t propertyIndex) override;
void propertyExtDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl,
uint16_t objectType, uint16_t objectInstance, uint16_t propertyId, uint8_t descriptionType, uint16_t propertyIndex) override;
void propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, uint8_t objectIndex,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) override;
void propertyValueExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl &secCtrl, ObjectType objectType, uint8_t objectInstance,

View File

@ -17,7 +17,6 @@ BauSystemBCoupler::BauSystemBCoupler(Platform& platform) :
_appLayer.transportLayer(_transLayer);
_transLayer.networkLayer(_netLayer);
_memory.addSaveRestore(&_deviceObj);
_memory.addSaveRestore(&_appProgram);
#ifdef USE_DATASECURE
_memory.addSaveRestore(&_secIfObj);
#endif

View File

@ -6,11 +6,17 @@
/*
cEMI Frame Format
+---------+--------+--------+--------+--------+---------+---------+--------+---------+
| Header | Msg |Add.Info| Ctrl 1 | Ctrl 2 | Source | Dest. | Data | APDU |
| | Code | Length | | | Address | Address | Length | |
+--------+--------+--------+--------+---------+---------+--------+---------+
| _data |
+--------+--------+--------+--------+---------+---------+--------+---------+
| LPDU |
+--------+--------+--------+--------+---------+---------+--------+---------+
| NPDU |
+---------+--------+--------+--------+--------+---------+---------+--------+---------+
6 bytes 1 byte 1 byte 1 byte 1 byte 2 bytes 2 bytes 1 byte 2 bytes
| Header | Msg |Add.Info| Ctrl 1 | Ctrl 2 | Source | Dest. | Data | TPDU |
| | Code | Length | | | Address | Address | Length | APDU |
+---------+--------+--------+--------+--------+---------+---------+--------+---------+
6 bytes 1 byte 1 byte 1 byte 1 byte 2 bytes 2 bytes 1 byte n bytes
Header = See below the structure of a cEMI header
Message Code = See below. On Appendix A is the list of all existing EMI and cEMI codes
@ -85,11 +91,11 @@ CemiFrame::CemiFrame(uint8_t apduLength)
_apdu(_data + APDU_LPDU_DIFF, *this)
{
_ctrl1 = _data + CEMI_HEADER_SIZE;
_length = 0;
memset(_data, 0, apduLength + APDU_LPDU_DIFF);
_ctrl1[0] |= Broadcast;
_npdu.octetCount(apduLength);
_length = _npdu.length() + NPDU_LPDU_DIFF;
}
CemiFrame::CemiFrame(const CemiFrame & other)
@ -116,6 +122,7 @@ CemiFrame& CemiFrame::operator=(CemiFrame other)
return *this;
}
MessageCode CemiFrame::messageCode() const
{
return (MessageCode)_data[0];
@ -128,9 +135,7 @@ void CemiFrame::messageCode(MessageCode msgCode)
uint16_t CemiFrame::totalLenght() const
{
uint16_t tmp =
_npdu.length() + NPDU_LPDU_DIFF;
return tmp;
return _length;
}
uint16_t CemiFrame::telegramLengthtTP() const
@ -369,14 +374,27 @@ bool CemiFrame::valid() const
uint8_t apduLen = _data[_data[1] + NPDU_LPDU_DIFF];
if (_length != 0 && _length != (addInfoLen + apduLen + NPDU_LPDU_DIFF + 2))
{
print("length issue, length: ");
print(_length);
print(" addInfoLen: ");
print(addInfoLen);
print(" apduLen: ");
print(apduLen);
print(" expected length: ");
println(addInfoLen + apduLen + NPDU_LPDU_DIFF + 2);
printHex("Frame: ", _data, _length, true);
return false;
}
if ((_ctrl1[0] & 0x40) > 0 // Bit 6 has do be 0
|| (_ctrl1[1] & 0xF) > 0 // only standard or extended frames
|| _npdu.octetCount() == 0xFF // not allowed
|| (_npdu.octetCount() > 15 && frameType() == StandardFrame)
)
){
print("Other issue");
return false;
}
return true;
}

View File

@ -11,10 +11,13 @@
#include <stdio.h>
CemiServer::CemiServer(BauSystemB& bau)
: _bau(bau),
_usbTunnelInterface(*this,
: _bau(bau)
#ifdef USE_USB
,
_usbTunnelInterface(*this,
_bau.deviceObject().maskVersion(),
_bau.deviceObject().manufacturerId())
#endif
{
// The cEMI server will hand out the device address + 1 to the cEMI client (e.g. ETS),
// so that the device and the cEMI client/server connection(tunnel) can operate simultaneously.
@ -26,6 +29,13 @@ void CemiServer::dataLinkLayer(DataLinkLayer& layer)
_dataLinkLayer = &layer;
}
#ifdef KNX_TUNNELING
void CemiServer::dataLinkLayerPrimary(DataLinkLayer& layer)
{
_dataLinkLayerPrimary = &layer;
}
#endif
uint16_t CemiServer::clientAddress() const
{
return _clientAddress;
@ -42,14 +52,20 @@ void CemiServer::dataConfirmationToTunnel(CemiFrame& frame)
frame.messageCode(L_data_con);
#ifdef KNX_LOG_TUNNELING
print("L_data_con: src: ");
print(frame.sourceAddress(), HEX);
print(" dst: ");
print(frame.destinationAddress(), HEX);
printHex(" frame: ", frame.data(), frame.dataLength());
#endif
#ifdef USE_USB
_usbTunnelInterface.sendCemiFrame(frame);
#elif defined(KNX_TUNNELING)
_dataLinkLayerPrimary->dataConfirmationToTunnel(frame);
#endif
frame.messageCode(backupMsgCode);
}
@ -59,7 +75,11 @@ void CemiServer::dataIndicationToTunnel(CemiFrame& frame)
#ifdef USE_RF
bool isRf = _dataLinkLayer->mediumType() == DptMedium::KNX_RF;
uint8_t data[frame.dataLength() + (isRf ? 10 : 0)];
#else
uint8_t data[frame.dataLength()];
#endif
#ifdef USE_RF
if (isRf)
{
data[0] = L_data_ind; // Message Code
@ -73,241 +93,51 @@ void CemiServer::dataIndicationToTunnel(CemiFrame& frame)
}
else
{
#endif
memcpy(&data[0], frame.data(), frame.dataLength());
#ifdef USE_RF
}
#else
uint8_t data[frame.dataLength()];
memcpy(&data[0], frame.data(), frame.dataLength());
#endif
CemiFrame tmpFrame(data, sizeof(data));
#ifdef KNX_LOG_TUNNELING
print("ToTunnel ");
print("L_data_ind: src: ");
print(tmpFrame.sourceAddress(), HEX);
print(" dst: ");
print(tmpFrame.destinationAddress(), HEX);
printHex(" frame: ", tmpFrame.data(), tmpFrame.dataLength());
#endif
tmpFrame.apdu().type();
#ifdef USE_USB
_usbTunnelInterface.sendCemiFrame(tmpFrame);
#elif defined(KNX_TUNNELING)
_dataLinkLayerPrimary->dataIndicationToTunnel(frame);
#endif
}
void CemiServer::frameReceived(CemiFrame& frame)
{
bool isRf = _dataLinkLayer->mediumType() == DptMedium::KNX_RF;
switch(frame.messageCode())
{
case L_data_req:
{
// Fill in the cEMI client address if the client sets
// source address to 0.
if(frame.sourceAddress() == 0x0000)
{
frame.sourceAddress(_clientAddress);
}
#ifdef USE_RF
if (isRf)
{
// Check if we have additional info for RF
if (((frame.data())[1] == 0x0A) && // Additional info total length: we only handle one additional info of type RF
((frame.data())[2] == 0x02) && // Additional info type: RF
((frame.data())[3] == 0x08) ) // Additional info length of type RF: 8 bytes (fixed)
{
frame.rfInfo((frame.data())[4]);
// Use the values provided in the RF additonal info
if ( ((frame.data())[5] != 0x00) || ((frame.data())[6] != 0x00) || ((frame.data())[7] != 0x00) ||
((frame.data())[8] != 0x00) || ((frame.data())[9] != 0x00) || ((frame.data())[10] != 0x00) )
{
frame.rfSerialOrDoA(&((frame.data())[5]));
} // else leave the nullptr as it is
frame.rfLfn((frame.data())[11]);
}
// If the cEMI client does not provide a link layer frame number (LFN),
// we use our own counter.
// Note: There is another link layer frame number counter inside the RF data link layer class!
// That counter is solely for the local application!
// If we set a LFN here, the data link layer counter is NOT used!
if (frame.rfLfn() == 0xFF)
{
// Set Data Link Layer Frame Number
frame.rfLfn(_frameNumber);
// Link Layer frame number counts 0..7
_frameNumber = (_frameNumber + 1) & 0x7;
}
}
#endif
print("L_data_req: src: ");
print(frame.sourceAddress(), HEX);
print(" dst: ");
print(frame.destinationAddress(), HEX);
printHex(" frame: ", frame.data(), frame.dataLength());
_dataLinkLayer->dataRequestFromTunnel(frame);
handleLData(frame);
break;
}
case M_PropRead_req:
{
print("M_PropRead_req: ");
uint16_t objectType;
popWord(objectType, &frame.data()[1]);
uint8_t objectInstance = frame.data()[3];
uint8_t propertyId = frame.data()[4];
uint8_t numberOfElements = frame.data()[5] >> 4;
uint16_t startIndex = frame.data()[6] | ((frame.data()[5]&0x0F)<<8);
uint8_t* data = nullptr;
uint32_t dataSize = 0;
print("ObjType: ");
print(objectType, DEC);
print(" ObjInst: ");
print(objectInstance, DEC);
print(" PropId: ");
print(propertyId, DEC);
print(" NoE: ");
print(numberOfElements, DEC);
print(" startIdx: ");
print(startIndex, DEC);
// propertyValueRead() allocates memory for the data! Needs to be deleted again!
_bau.propertyValueRead((ObjectType)objectType, objectInstance, propertyId, numberOfElements, startIndex, &data, dataSize);
// Patch result for device address in device object
// The cEMI server will hand out the device address + 1 to the cEMI client (e.g. ETS),
// so that the device and the cEMI client/server connection(tunnel) can operate simultaneously.
// KNX IP Interfaces which offer multiple simultaneous tunnel connections seem to operate the same way.
// Each tunnel has its own cEMI client address which is based on the main device address.
if (((ObjectType) objectType == OT_DEVICE) &&
(propertyId == PID_DEVICE_ADDR) &&
(numberOfElements == 1))
{
data[0] = (uint8_t) (_clientAddress & 0xFF);
}
else if (((ObjectType) objectType == OT_DEVICE) &&
(propertyId == PID_SUBNET_ADDR) &&
(numberOfElements == 1))
{
data[0] = (uint8_t) ((_clientAddress >> 8) & 0xFF);
}
if (data && dataSize && numberOfElements)
{
printHex(" <- data: ", data, dataSize);
println("");
// Prepare positive response
uint8_t responseData[7 + dataSize];
memcpy(responseData, frame.data(), 7);
memcpy(&responseData[7], data, dataSize);
CemiFrame responseFrame(responseData, sizeof(responseData));
responseFrame.messageCode(M_PropRead_con);
_usbTunnelInterface.sendCemiFrame(responseFrame);
delete[] data;
}
else
{
// Prepare negative response
uint8_t responseData[7 + 1];
memcpy(responseData, frame.data(), sizeof(responseData));
responseData[7] = Void_DP; // Set cEMI error code
responseData[5] = 0; // Set Number of elements to zero
printHex(" <- error: ", &responseData[7], 1);
println("");
CemiFrame responseFrame(responseData, sizeof(responseData));
responseFrame.messageCode(M_PropRead_con);
_usbTunnelInterface.sendCemiFrame(responseFrame);
}
handleMPropRead(frame);
break;
}
case M_PropWrite_req:
{
print("M_PropWrite_req: ");
uint16_t objectType;
popWord(objectType, &frame.data()[1]);
uint8_t objectInstance = frame.data()[3];
uint8_t propertyId = frame.data()[4];
uint8_t numberOfElements = frame.data()[5] >> 4;
uint16_t startIndex = frame.data()[6] | ((frame.data()[5]&0x0F)<<8);
uint8_t* requestData = &frame.data()[7];
uint32_t requestDataSize = frame.dataLength() - 7;
print("ObjType: ");
print(objectType, DEC);
print(" ObjInst: ");
print(objectInstance, DEC);
print(" PropId: ");
print(propertyId, DEC);
print(" NoE: ");
print(numberOfElements, DEC);
print(" startIdx: ");
print(startIndex, DEC);
printHex(" -> data: ", requestData, requestDataSize);
// Patch request for device address in device object
if (((ObjectType) objectType == OT_DEVICE) &&
(propertyId == PID_DEVICE_ADDR) &&
(numberOfElements == 1))
{
// Temporarily store new cEMI client address in memory
// We also be sent back if the client requests it again
_clientAddress = (_clientAddress & 0xFF00) | requestData[0];
print("cEMI client address: ");
println(_clientAddress, HEX);
}
else if (((ObjectType) objectType == OT_DEVICE) &&
(propertyId == PID_SUBNET_ADDR) &&
(numberOfElements == 1))
{
// Temporarily store new cEMI client address in memory
// We also be sent back if the client requests it again
_clientAddress = (_clientAddress & 0x00FF) | (requestData[0] << 8);
print("cEMI client address: ");
println(_clientAddress, HEX);
}
else
{
_bau.propertyValueWrite((ObjectType)objectType, objectInstance, propertyId, numberOfElements, startIndex, requestData, requestDataSize);
}
if (numberOfElements)
{
// Prepare positive response
uint8_t responseData[7];
memcpy(responseData, frame.data(), sizeof(responseData));
println(" <- no error");
CemiFrame responseFrame(responseData, sizeof(responseData));
responseFrame.messageCode(M_PropWrite_con);
_usbTunnelInterface.sendCemiFrame(responseFrame);
}
else
{
// Prepare negative response
uint8_t responseData[7 + 1];
memcpy(responseData, frame.data(), sizeof(responseData));
responseData[7] = Illegal_Command; // Set cEMI error code
responseData[5] = 0; // Set Number of elements to zero
printHex(" <- error: ", &responseData[7], 1);
println("");
CemiFrame responseFrame(responseData, sizeof(responseData));
responseFrame.messageCode(M_PropWrite_con);
_usbTunnelInterface.sendCemiFrame(responseFrame);
}
handleMPropWrite(frame);
break;
}
@ -325,16 +155,7 @@ void CemiServer::frameReceived(CemiFrame& frame)
case M_Reset_req:
{
println("M_Reset_req: sending M_Reset_ind");
// A real device reset does not work for USB or KNXNET/IP.
// Thus, M_Reset_ind is NOT mandatory for USB and KNXNET/IP.
// We just save all data to the EEPROM
_bau.writeMemory();
// Prepare response
uint8_t responseData[1];
CemiFrame responseFrame(responseData, sizeof(responseData));
responseFrame.messageCode(M_Reset_ind);
_usbTunnelInterface.sendCemiFrame(responseFrame);
handleMReset(frame);
break;
}
@ -352,9 +173,263 @@ void CemiServer::frameReceived(CemiFrame& frame)
}
}
void CemiServer::handleLData(CemiFrame& frame)
{
// Fill in the cEMI client address if the client sets
// source address to 0.
#ifndef KNX_TUNNELING
//We already set the correct IA
if(frame.sourceAddress() == 0x0000)
{
frame.sourceAddress(_clientAddress);
}
#endif
#ifdef USE_RF
if (_dataLinkLayer->mediumType() == DptMedium::KNX_RF)
{
// Check if we have additional info for RF
if (((frame.data())[1] == 0x0A) && // Additional info total length: we only handle one additional info of type RF
((frame.data())[2] == 0x02) && // Additional info type: RF
((frame.data())[3] == 0x08) ) // Additional info length of type RF: 8 bytes (fixed)
{
frame.rfInfo((frame.data())[4]);
// Use the values provided in the RF additonal info
if ( ((frame.data())[5] != 0x00) || ((frame.data())[6] != 0x00) || ((frame.data())[7] != 0x00) ||
((frame.data())[8] != 0x00) || ((frame.data())[9] != 0x00) || ((frame.data())[10] != 0x00) )
{
frame.rfSerialOrDoA(&((frame.data())[5]));
} // else leave the nullptr as it is
frame.rfLfn((frame.data())[11]);
}
// If the cEMI client does not provide a link layer frame number (LFN),
// we use our own counter.
// Note: There is another link layer frame number counter inside the RF data link layer class!
// That counter is solely for the local application!
// If we set a LFN here, the data link layer counter is NOT used!
if (frame.rfLfn() == 0xFF)
{
// Set Data Link Layer Frame Number
frame.rfLfn(_frameNumber);
// Link Layer frame number counts 0..7
_frameNumber = (_frameNumber + 1) & 0x7;
}
}
#endif
#ifdef KNX_LOG_TUNNELING
print("L_data_req: src: ");
print(frame.sourceAddress(), HEX);
print(" dst: ");
print(frame.destinationAddress(), HEX);
printHex(" frame: ", frame.data(), frame.dataLength());
#endif
_dataLinkLayer->dataRequestFromTunnel(frame);
}
void CemiServer::handleMPropRead(CemiFrame& frame)
{
#ifdef KNX_LOG_TUNNELING
print("M_PropRead_req: ");
#endif
uint16_t objectType;
popWord(objectType, &frame.data()[1]);
uint8_t objectInstance = frame.data()[3];
uint8_t propertyId = frame.data()[4];
uint8_t numberOfElements = frame.data()[5] >> 4;
uint16_t startIndex = frame.data()[6] | ((frame.data()[5]&0x0F)<<8);
uint8_t* data = nullptr;
uint32_t dataSize = 0;
#ifdef KNX_LOG_TUNNELING
print("ObjType: ");
print(objectType, DEC);
print(" ObjInst: ");
print(objectInstance, DEC);
print(" PropId: ");
print(propertyId, DEC);
print(" NoE: ");
print(numberOfElements, DEC);
print(" startIdx: ");
print(startIndex, DEC);
#endif
// propertyValueRead() allocates memory for the data! Needs to be deleted again!
_bau.propertyValueRead((ObjectType)objectType, objectInstance, propertyId, numberOfElements, startIndex, &data, dataSize);
// Patch result for device address in device object
// The cEMI server will hand out the device address + 1 to the cEMI client (e.g. ETS),
// so that the device and the cEMI client/server connection(tunnel) can operate simultaneously.
// KNX IP Interfaces which offer multiple simultaneous tunnel connections seem to operate the same way.
// Each tunnel has its own cEMI client address which is based on the main device address.
if (((ObjectType) objectType == OT_DEVICE) &&
(propertyId == PID_DEVICE_ADDR) &&
(numberOfElements == 1))
{
data[0] = (uint8_t) (_clientAddress & 0xFF);
}
else if (((ObjectType) objectType == OT_DEVICE) &&
(propertyId == PID_SUBNET_ADDR) &&
(numberOfElements == 1))
{
data[0] = (uint8_t) ((_clientAddress >> 8) & 0xFF);
}
if (data && dataSize && numberOfElements)
{
#ifdef KNX_LOG_TUNNELING
printHex(" <- data: ", data, dataSize);
#endif
// Prepare positive response
uint8_t responseData[7 + dataSize];
memcpy(responseData, frame.data(), 7);
memcpy(&responseData[7], data, dataSize);
CemiFrame responseFrame(responseData, sizeof(responseData));
responseFrame.messageCode(M_PropRead_con);
#ifdef USE_USB
_usbTunnelInterface.sendCemiFrame(responseFrame);
#elif defined(KNX_TUNNELING)
_dataLinkLayerPrimary->dataRequestToTunnel(responseFrame);
#endif
delete[] data;
}
else
{
// Prepare negative response
uint8_t responseData[7 + 1];
memcpy(responseData, frame.data(), sizeof(responseData));
responseData[7] = Void_DP; // Set cEMI error code
responseData[5] = 0; // Set Number of elements to zero
printHex(" <- error: ", &responseData[7], 1);
println("");
CemiFrame responseFrame(responseData, sizeof(responseData));
responseFrame.messageCode(M_PropRead_con);
#ifdef USE_USB
_usbTunnelInterface.sendCemiFrame(responseFrame);
#elif defined(KNX_TUNNELING)
_dataLinkLayerPrimary->dataRequestToTunnel(responseFrame);
#endif
}
}
void CemiServer::handleMPropWrite(CemiFrame& frame)
{
print("M_PropWrite_req: ");
uint16_t objectType;
popWord(objectType, &frame.data()[1]);
uint8_t objectInstance = frame.data()[3];
uint8_t propertyId = frame.data()[4];
uint8_t numberOfElements = frame.data()[5] >> 4;
uint16_t startIndex = frame.data()[6] | ((frame.data()[5]&0x0F)<<8);
uint8_t* requestData = &frame.data()[7];
uint32_t requestDataSize = frame.dataLength() - 7;
print("ObjType: ");
print(objectType, DEC);
print(" ObjInst: ");
print(objectInstance, DEC);
print(" PropId: ");
print(propertyId, DEC);
print(" NoE: ");
print(numberOfElements, DEC);
print(" startIdx: ");
print(startIndex, DEC);
printHex(" -> data: ", requestData, requestDataSize);
// Patch request for device address in device object
if (((ObjectType) objectType == OT_DEVICE) &&
(propertyId == PID_DEVICE_ADDR) &&
(numberOfElements == 1))
{
// Temporarily store new cEMI client address in memory
// We also be sent back if the client requests it again
_clientAddress = (_clientAddress & 0xFF00) | requestData[0];
print("cEMI client address: ");
println(_clientAddress, HEX);
}
else if (((ObjectType) objectType == OT_DEVICE) &&
(propertyId == PID_SUBNET_ADDR) &&
(numberOfElements == 1))
{
// Temporarily store new cEMI client address in memory
// We also be sent back if the client requests it again
_clientAddress = (_clientAddress & 0x00FF) | (requestData[0] << 8);
print("cEMI client address: ");
println(_clientAddress, HEX);
}
else
{
_bau.propertyValueWrite((ObjectType)objectType, objectInstance, propertyId, numberOfElements, startIndex, requestData, requestDataSize);
}
if (numberOfElements)
{
// Prepare positive response
uint8_t responseData[7];
memcpy(responseData, frame.data(), sizeof(responseData));
println(" <- no error");
CemiFrame responseFrame(responseData, sizeof(responseData));
responseFrame.messageCode(M_PropWrite_con);
#ifdef USE_USB
_usbTunnelInterface.sendCemiFrame(responseFrame);
#elif defined(KNX_TUNNELING)
_dataLinkLayerPrimary->dataRequestToTunnel(responseFrame);
#endif
}
else
{
// Prepare negative response
uint8_t responseData[7 + 1];
memcpy(responseData, frame.data(), sizeof(responseData));
responseData[7] = Illegal_Command; // Set cEMI error code
responseData[5] = 0; // Set Number of elements to zero
printHex(" <- error: ", &responseData[7], 1);
println("");
CemiFrame responseFrame(responseData, sizeof(responseData));
responseFrame.messageCode(M_PropWrite_con);
#ifdef USE_USB
_usbTunnelInterface.sendCemiFrame(responseFrame);
#elif defined(KNX_TUNNELING)
_dataLinkLayerPrimary->dataRequestToTunnel(responseFrame);
#endif
}
}
void CemiServer::handleMReset(CemiFrame& frame)
{
println("M_Reset_req: sending M_Reset_ind");
// A real device reset does not work for USB or KNXNET/IP.
// Thus, M_Reset_ind is NOT mandatory for USB and KNXNET/IP.
// We just save all data to the EEPROM
_bau.writeMemory();
// Prepare response
uint8_t responseData[1];
CemiFrame responseFrame(responseData, sizeof(responseData));
responseFrame.messageCode(M_Reset_ind);
#ifdef USE_USB
_usbTunnelInterface.sendCemiFrame(responseFrame);
#elif defined(KNX_TUNNELING)
_dataLinkLayerPrimary->dataRequestToTunnel(responseFrame);
#endif
}
void CemiServer::loop()
{
#ifdef USE_USB
_usbTunnelInterface.loop();
#endif
}
#endif

View File

@ -29,6 +29,9 @@ class CemiServer
CemiServer(BauSystemB& bau);
void dataLinkLayer(DataLinkLayer& layer);
#ifdef KNX_TUNNELING
void dataLinkLayerPrimary(DataLinkLayer& layer);
#endif
// from data link layer
// Only L_Data service
@ -47,9 +50,19 @@ class CemiServer
uint16_t _clientAddress = 0;
uint8_t _frameNumber = 0;
void handleLData(CemiFrame& frame);
void handleMPropRead(CemiFrame& frame);
void handleMPropWrite(CemiFrame& frame);
void handleMReset(CemiFrame& frame);
DataLinkLayer* _dataLinkLayer = nullptr;
#ifdef KNX_TUNNELING
DataLinkLayer* _dataLinkLayerPrimary = nullptr;
#endif
BauSystemB& _bau;
#ifdef USE_USB
UsbTunnelInterface _usbTunnelInterface;
#endif
};
#endif

View File

@ -52,7 +52,7 @@
// cEMI options
//#define USE_USB
//#define USE_CEMI_SERVER
#ifdef USE_USB
#if defined(USE_USB) || defined(KNX_TUNNELING)
#define USE_CEMI_SERVER
#endif

View File

@ -18,8 +18,8 @@ void DataLinkLayerCallbacks::setActivityCallback(ActivityCallback activityCallba
_activityCallback = activityCallback;
}
DataLinkLayer::DataLinkLayer(DeviceObject& devObj, NetworkLayerEntity& netLayerEntity, Platform& platform) :
_deviceObject(devObj), _networkLayerEntity(netLayerEntity), _platform(platform)
DataLinkLayer::DataLinkLayer(DeviceObject& devObj, NetworkLayerEntity& netLayerEntity, Platform& platform, BusAccessUnit& busAccessUnit) :
_deviceObject(devObj), _networkLayerEntity(netLayerEntity), _platform(platform), _bau(busAccessUnit)
{
#ifdef KNX_ACTIVITYCALLBACK
_netIndex = netLayerEntity.getEntityIndex();
@ -33,15 +33,59 @@ void DataLinkLayer::cemiServer(CemiServer& cemiServer)
_cemiServer = &cemiServer;
}
#ifdef KNX_TUNNELING
void DataLinkLayer::dataRequestToTunnel(CemiFrame& frame)
{
println("default dataRequestToTunnel");
}
void DataLinkLayer::dataConfirmationToTunnel(CemiFrame& frame)
{
println("default dataConfirmationToTunnel");
}
void DataLinkLayer::dataIndicationToTunnel(CemiFrame& frame)
{
println("default dataIndicationToTunnel");
}
bool DataLinkLayer::isTunnelAddress(uint16_t addr)
{
println("default IsTunnelAddress");
return false;
}
#endif
void DataLinkLayer::dataRequestFromTunnel(CemiFrame& frame)
{
_cemiServer->dataConfirmationToTunnel(frame);
frame.messageCode(L_data_ind);
// Send to local stack
// Send to local stack ( => cemiServer for potential other tunnel and network layer for routing)
frameReceived(frame);
#ifdef KNX_TUNNELING
// TunnelOpti
// Optimize performance when receiving unicast data over tunnel wich is not meant to be used on the physical TP line
// dont send to knx when
// frame is individual adressed AND
// destionation == PA of Tunnel-Server OR
// destination == PA of a Tunnel OR (TODO)
// destination is not the TP/secondary line/segment but IP/primary (TODO)
if(frame.addressType() == AddressType::IndividualAddress)
{
if(frame.destinationAddress() == _deviceObject.individualAddress())
return;
if(isRoutedPA(frame.destinationAddress()))
return;
if(isTunnelingPA(frame.destinationAddress()))
return;
}
#endif
// Send to KNX medium
sendFrame(frame);
}
@ -111,12 +155,24 @@ void DataLinkLayer::frameReceived(CemiFrame& frame)
#ifdef USE_CEMI_SERVER
// Do not send our own message back to the tunnel
#ifdef KNX_TUNNELING
//we dont need to check it here
// send inbound frames to the tunnel if we are the secondary (TP) interface
if( _networkLayerEntity.getEntityIndex() == 1)
_cemiServer->dataIndicationToTunnel(frame);
#else
if (frame.sourceAddress() != _cemiServer->clientAddress())
{
_cemiServer->dataIndicationToTunnel(frame);
}
#endif
#endif
// print("Frame received destination: ");
// print(destination, 16);
// println();
// print("frameReceived: frame valid? :");
// println(npdu.frame().valid() ? "true" : "false");
if (source == ownAddr)
_deviceObject.individualAddressDuplication(true);
@ -133,15 +189,17 @@ void DataLinkLayer::frameReceived(CemiFrame& frame)
}
}
bool DataLinkLayer::sendTelegram(NPDU & npdu, AckType ack, uint16_t destinationAddr, AddressType addrType, uint16_t sourceAddr, FrameFormat format, Priority priority, SystemBroadcast systemBroadcast)
bool DataLinkLayer::sendTelegram(NPDU & npdu, AckType ack, uint16_t destinationAddr, AddressType addrType, uint16_t sourceAddr, FrameFormat format, Priority priority, SystemBroadcast systemBroadcast, bool doNotRepeat)
{
CemiFrame& frame = npdu.frame();
// print("Send telegram frame valid ?: ");
// println(frame.valid()?"true":"false");
frame.messageCode(L_data_ind);
frame.destinationAddress(destinationAddr);
frame.sourceAddress(sourceAddr);
frame.addressType(addrType);
frame.priority(priority);
frame.repetition(RepetitionAllowed);
frame.repetition(doNotRepeat?NoRepitiion:RepetitionAllowed);
frame.systemBroadcast(systemBroadcast);
if (npdu.octetCount() <= 15)
@ -162,11 +220,29 @@ bool DataLinkLayer::sendTelegram(NPDU & npdu, AckType ack, uint16_t destinationA
// frame.apdu().printPDU();
// }
bool sendTheFrame = true;
bool success = true;
#ifdef KNX_TUNNELING
// TunnelOpti
// Optimize performance when sending unicast data over tunnel wich is not meant to be used on the physical TP line
// dont send to knx when
// a) we are the secondary interface (e.g. TP) AND
// b) destination == PA of a Tunnel (TODO)
if(_networkLayerEntity.getEntityIndex() == 1 && addrType == AddressType::IndividualAddress) // don't send to tp if we are the secondary (TP) interface AND the destination is a tunnel-PA
{
if(isTunnelingPA(destinationAddr))
sendTheFrame = false;
}
#endif
// The data link layer might be an open media link layer
// and will setup rfSerialOrDoA, rfInfo and rfLfn that we also
// have to send through the cEMI server tunnel
// Thus, reuse the modified cEMI frame as "frame" is only passed by reference here!
bool success = sendFrame(frame);
if(sendTheFrame)
success = sendFrame(frame);
#ifdef USE_CEMI_SERVER
CemiFrame tmpFrame(frame.data(), frame.totalLenght());
@ -179,7 +255,9 @@ bool DataLinkLayer::sendTelegram(NPDU & npdu, AckType ack, uint16_t destinationA
tmpFrame.rfLfn(frame.rfLfn());
#endif
tmpFrame.confirm(ConfirmNoError);
_cemiServer->dataIndicationToTunnel(tmpFrame);
if(_networkLayerEntity.getEntityIndex() == 1) // only send to tunnel if we are the secondary (TP) interface
_cemiServer->dataIndicationToTunnel(tmpFrame);
#endif
return success;
@ -190,4 +268,48 @@ uint8_t* DataLinkLayer::frameData(CemiFrame& frame)
return frame._data;
}
#ifdef KNX_TUNNELING
bool DataLinkLayer::isTunnelingPA(uint16_t pa)
{
uint8_t num = KNX_TUNNELING;
uint32_t len = 0;
uint8_t* data = nullptr;
_bau.propertyValueRead(OT_IP_PARAMETER, 0, PID_ADDITIONAL_INDIVIDUAL_ADDRESSES, num, 1, &data, len);
//printHex("isTunnelingPA, PID_ADDITIONAL_INDIVIDUAL_ADDRESSES: ", *data, len);
if(len != KNX_TUNNELING * 2)
{
println("Tunnel PAs unkwnown");
if(data != nullptr)
delete[] data;
return false;
}
for(uint8_t i = 0; i < KNX_TUNNELING; i++)
{
uint16_t tunnelpa;
popWord(tunnelpa, (data)+i*2);
if(pa == tunnelpa)
{
if(data != nullptr)
delete[] data;
return true;
}
}
if(data != nullptr)
delete[] data;
return false;
}
bool DataLinkLayer::isRoutedPA(uint16_t pa)
{
uint16_t ownpa = _deviceObject.individualAddress();
uint16_t own_sm;
if ((ownpa & 0x0F00) == 0x0)
own_sm = 0xF000;
else
own_sm = 0xFF00;
return (pa & own_sm) != ownpa;
}
#endif

View File

@ -7,6 +7,7 @@
#include "knx_types.h"
#include "network_layer_entity.h"
#include "cemi_server.h"
#include "bau.h"
class Platform;
@ -26,12 +27,18 @@ class DataLinkLayer
{
public:
DataLinkLayer(DeviceObject& devObj, NetworkLayerEntity& netLayerEntity,
Platform& platform);
Platform& platform, BusAccessUnit& busAccessUnit);
#ifdef USE_CEMI_SERVER
// from tunnel
void cemiServer(CemiServer& cemiServer);
void dataRequestFromTunnel(CemiFrame& frame);
#ifdef KNX_TUNNELING
virtual void dataRequestToTunnel(CemiFrame& frame);
virtual void dataConfirmationToTunnel(CemiFrame& frame);
virtual void dataIndicationToTunnel(CemiFrame& frame);
virtual bool isTunnelAddress(uint16_t addr);
#endif
#endif
// from network layer
@ -46,16 +53,21 @@ class DataLinkLayer
protected:
void frameReceived(CemiFrame& frame);
void dataConReceived(CemiFrame& frame, bool success);
bool sendTelegram(NPDU& npdu, AckType ack, uint16_t destinationAddr, AddressType addrType, uint16_t sourceAddr, FrameFormat format, Priority priority, SystemBroadcast systemBroadcast);
bool sendTelegram(NPDU& npdu, AckType ack, uint16_t destinationAddr, AddressType addrType, uint16_t sourceAddr, FrameFormat format, Priority priority, SystemBroadcast systemBroadcast, bool doNotRepeat = false);
virtual bool sendFrame(CemiFrame& frame) = 0;
uint8_t* frameData(CemiFrame& frame);
DeviceObject& _deviceObject;
NetworkLayerEntity& _networkLayerEntity;
Platform& _platform;
BusAccessUnit& _bau;
#ifdef USE_CEMI_SERVER
CemiServer* _cemiServer;
#endif
#ifdef KNX_ACTIVITYCALLBACK
uint8_t _netIndex = 0;
#endif
#ifdef KNX_TUNNELING
bool isTunnelingPA(uint16_t pa);
bool isRoutedPA(uint16_t pa);
#endif
};

View File

@ -43,5 +43,9 @@ public:
uint8_t defaultHopCount();
private:
uint8_t _prgMode = 0;
uint16_t _ownAddress = 65535; // 15.15.255;
#if MASK_VERSION == 0x091A || MASK_VERSION == 0x2920
uint16_t _ownAddress = 0xFF00; // 15.15.0; couplers have 15.15.0 as default PA
#else
uint16_t _ownAddress = 0xFFFF; // 15.15.255;
#endif
};

View File

@ -87,6 +87,9 @@ int KNX_Decode_Value(uint8_t* payload, size_t payload_length, const Dpt& datatyp
// DPT 26.* - Scene Info
if (datatype.mainGroup == 26 && datatype.subGroup == 1 && datatype.index <= 1)
return busValueToSceneInfo(payload, payload_length, datatype, value);
// DPT 27.001 - 32 Bit field
if (datatype.mainGroup == 27 && datatype.subGroup == 1 && !datatype.index)
return busValueToSigned32(payload, payload_length, datatype, value);
// DPT 28.* - Unicode String
if (datatype.mainGroup == 28 && datatype.subGroup == 1 && !datatype.index)
return busValueToUnicode(payload, payload_length, datatype, value);
@ -205,7 +208,10 @@ int KNX_Encode_Value(const KNXValue& value, uint8_t* payload, size_t payload_len
return valueToBusValueDateTime(value, payload, payload_length, datatype);
// DPT 26.* - Scene Info
if (datatype.mainGroup == 26 && datatype.subGroup == 1 && datatype.index <= 1)
return valueToBusValueSceneInfo(value, payload, payload_length, datatype);
return valueToBusValueSceneInfo(value, payload, payload_length, datatype);
// DPT 27.001 - 32 Bit Field
if (datatype.mainGroup == 27 && datatype.subGroup == 1 && !datatype.index)
return valueToBusValueUnsigned32(value, payload, payload_length, datatype);
// DPT 28.* - Unicode String
if (datatype.mainGroup == 28 && datatype.subGroup == 1 && !datatype.index)
return valueToBusValueUnicode(value, payload, payload_length, datatype);
@ -517,15 +523,13 @@ int busValueToAccess(const uint8_t* payload, size_t payload_length, const Dpt& d
int busValueToString(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value)
{
ASSERT_PAYLOAD(14);
char strValue[15];
strValue[14] = '\0';
for (int n = 0; n < 14; ++n)
{
strValue[n] = signed8FromPayload(payload, n);
if (!datatype.subGroup && (strValue[n] & 0x80))
auto value = signed8FromPayload(payload, n);
if (!datatype.subGroup && (value & 0x80))
return false;
}
value = strValue;
value = (const char*) payload;
return true;
}

View File

@ -20,18 +20,6 @@ GroupObject::GroupObject()
#endif
}
GroupObject::GroupObject(const GroupObject& other)
{
_data = new uint8_t[other._dataLength];
_commFlagEx = other._commFlagEx;
_dataLength = other._dataLength;
_asap = other._asap;
#ifndef SMALL_GROUPOBJECT
_updateHandler = other._updateHandler;
#endif
memcpy(_data, other._data, _dataLength);
}
GroupObject::~GroupObject()
{
if (_data)
@ -114,12 +102,12 @@ size_t GroupObject::goSize()
size_t size = sizeInTelegram();
if (size == 0)
return 1;
return size;
}
// see knxspec 3.5.1 p. 178
size_t GroupObject::asapValueSize(uint8_t code)
size_t GroupObject::asapValueSize(uint8_t code) const
{
if (code < 7)
return 0;
@ -194,6 +182,17 @@ size_t GroupObject::sizeInTelegram()
return asapValueSize(code);
}
size_t GroupObject::sizeInMemory() const
{
uint8_t code = lowByte(ntohs(_table->_tableData[_asap]));
size_t result = asapValueSize(code);
if (code == 0)
return 1;
if (code == 14)
return 14 + 1;
return result;
}
#ifdef SMALL_GROUPOBJECT
GroupObjectUpdatedHandler GroupObject::classCallback()
{
@ -310,4 +309,14 @@ bool GroupObject::valueNoSendCompare(const KNXValue& value, const Dpt& type)
return dataChanged;
}
}
bool GroupObject::valueCompare(const KNXValue& value, const Dpt& type)
{
if (valueNoSendCompare(value, type))
{
objectWritten();
return true;
}
return false;
}

View File

@ -7,12 +7,6 @@
class GroupObjectTableObject;
/**
* LIMITATION: The differentiation between uninitialized and initialized state can NOT be represented correctly in ComFlag alone:
* It might be in state Transmitting during a ReadRequest on startup while value is still not valid.
*
* See ComFlagEx for a clear uninitialized handling.
*/
enum ComFlag : uint8_t
{
Updated = 0, //!< Group object was updated
@ -21,17 +15,13 @@ enum ComFlag : uint8_t
Transmitting = 3, //!< Group Object is processed a the moment (read or write)
Ok = 4, //!< read or write request were send successfully
Error = 5, //!< there was an error on processing a request
Uninitialized = 6 //!< uninitialized Group Object, its value is not valid; WARNING: Other Values do NOT guarantee an actual valid value!
Uninitialized = 6 //!< uninitialized Group Object, its value is not valid
};
/**
* Extended ComFlag
* Add a separate uninitialized flag to overcome the limitations of ComFlag.
*
* Implementation Note:
* We use MSB to store uninitialized state and keep the size of GroupObject the same saving memory resources.
* The old uninitialized handling is not changed for compatibility reasons.
*/
// extended ComFlag: Uninitialized it not handled correctly as ComFlag
// it might be in state Transmitting during a ReadRequest on startup while value is still not valid
// we use MSB to store Uninitialized and keep the size of GroupObject the same saving memory ressources
// the old Uninitialized handling is still there for compatibility reasons.
struct ComFlagEx
{
bool uninitialized : 1;
@ -67,10 +57,6 @@ class GroupObject
* The constructor.
*/
GroupObject();
/**
* The copy constructor.
*/
GroupObject(const GroupObject& other);
/**
* The destructor.
*/
@ -149,6 +135,11 @@ class GroupObject
* will return 0.
*/
size_t sizeInTelegram();
/**
* returns the size of the group object in the heap memory of the group object. The function returns the same value as goSize(),
* exept fot the 14 byte string type to reserve one byte of a \0 terminator character.
*/
size_t sizeInMemory() const;
/**
* returns the pointer to the value of the group object. This can be used if a datapoint type is not supported or if you want do
* your own conversion.
@ -183,6 +174,19 @@ class GroupObject
* The parameters must fit the group object. Otherwise it will stay unchanged.
*/
void 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.
* Use this method only, when the value should not be sent if it was not changed, otherwise value(const KNXValue&, const Dpt&) will do the same (without overhead for comparing)
* @param value the value the group object is set to
* @param type the datapoint type used for the conversion.
*
* The parameters must fit the group object. Otherwise it will stay unchanged.
*
* @returns true if the value of the group object has changed
*/
bool valueCompare(const KNXValue& value, const Dpt& type);
/**
* set the current value of the group object.
* @param value the value the group object is set to
@ -271,7 +275,7 @@ class GroupObject
static GroupObjectUpdatedHandler _updateHandlerStatic;
#endif
size_t asapValueSize(uint8_t code);
size_t asapValueSize(uint8_t code) const;
size_t goSize();
uint16_t _asap = 0;
ComFlagEx _commFlagEx;

View File

@ -107,10 +107,11 @@ bool GroupObjectTableObject::initGroupObjects()
GroupObject& go = _groupObjects[asap - 1];
go._asap = asap;
go._table = this;
go._dataLength = go.goSize();
go._data = new uint8_t[go._dataLength];
memset(go._data, 0, go._dataLength);
size_t sizeInMemory = go.sizeInMemory();
go._data = new uint8_t[sizeInMemory];
memset(go._data, 0, sizeInMemory);
if (go.valueReadOnInit())
go.requestObjectRead();

View File

@ -60,6 +60,20 @@ void InterfaceObject::masterReset(EraseCode eraseCode, uint8_t channel)
// However, for the time being we provide an empty default implementation
}
void InterfaceObject::readPropertyLength(PropertyID id, uint16_t &length)
{
uint8_t count = 1;
uint16_t propval = 0;
readProperty(id, 0, count, (uint8_t*)&propval);
if(count == 0)
{
length = 0;
return;
}
length = ntohs(propval);
}
void InterfaceObject::readProperty(PropertyID id, uint16_t start, uint8_t& count, uint8_t* data)
{
Property* prop = property(id);

View File

@ -4,6 +4,7 @@
#include "property.h"
#include "save_restore.h"
#include "knx_types.h"
#include "bits.h"
/** Enum for the type of an interface object. See Section 2.2 of knx:3/7/3 */
enum ObjectType
@ -54,7 +55,10 @@ enum ObjectType
OT_SECURITY = 17,
/** RF Medium Object */
OT_RF_MEDIUM = 19
OT_RF_MEDIUM = 19,
/** Dummy so this enum is 16bit */
OT_DUMMY = 0xFFFF
};
/**
@ -67,6 +71,14 @@ class InterfaceObject : public SaveRestore
* Destructor
*/
virtual ~InterfaceObject();
/**
* Read length of a property of the interface object. See section 4.8.4.2 of @cite knx:3/4/1.
*
* @param id id of the property to read
*
* @param[out] length length of the requested property
*/
virtual void readPropertyLength(PropertyID id, uint16_t &length);
/**
* Read a property of the interface object. See section 4.8.4.2 of @cite knx:3/4/1.
*

View File

@ -9,6 +9,22 @@
#include "knx_ip_routing_indication.h"
#include "knx_ip_search_request.h"
#include "knx_ip_search_response.h"
#include "knx_ip_search_request_extended.h"
#include "knx_ip_search_response_extended.h"
#include "knx_facade.h"
#ifdef KNX_TUNNELING
#include "knx_ip_connect_request.h"
#include "knx_ip_connect_response.h"
#include "knx_ip_state_request.h"
#include "knx_ip_state_response.h"
#include "knx_ip_disconnect_request.h"
#include "knx_ip_disconnect_response.h"
#include "knx_ip_tunneling_request.h"
#include "knx_ip_tunneling_ack.h"
#include "knx_ip_description_request.h"
#include "knx_ip_description_response.h"
#include "knx_ip_config_request.h"
#endif
#include <stdio.h>
#include <string.h>
@ -19,7 +35,7 @@
#define MIN_LEN_CEMI 10
IpDataLinkLayer::IpDataLinkLayer(DeviceObject& devObj, IpParameterObject& ipParam,
NetworkLayerEntity &netLayerEntity, Platform& platform, DataLinkLayerCallbacks* dllcb) : DataLinkLayer(devObj, netLayerEntity, platform), _ipParameters(ipParam), _dllcb(dllcb)
NetworkLayerEntity &netLayerEntity, Platform& platform, BusAccessUnit& busAccessUnit, DataLinkLayerCallbacks* dllcb) : DataLinkLayer(devObj, netLayerEntity, platform, busAccessUnit), _ipParameters(ipParam), _dllcb(dllcb)
{
}
@ -38,13 +54,241 @@ bool IpDataLinkLayer::sendFrame(CemiFrame& frame)
return success;
}
#ifdef KNX_TUNNELING
void IpDataLinkLayer::dataRequestToTunnel(CemiFrame& frame)
{
if(frame.addressType() == AddressType::GroupAddress)
{
for(int i = 0; i < KNX_TUNNELING; i++)
if(tunnels[i].ChannelId != 0 && tunnels[i].IndividualAddress == frame.sourceAddress())
sendFrameToTunnel(&tunnels[i], frame);
//TODO check if source is from tunnel
return;
}
KnxIpTunnelConnection *tun = nullptr;
for(int i = 0; i < KNX_TUNNELING; i++)
{
if(tunnels[i].IndividualAddress == frame.sourceAddress())
continue;
if(tunnels[i].IndividualAddress == frame.destinationAddress())
{
tun = &tunnels[i];
break;
}
}
if(tun == nullptr)
{
for(int i = 0; i < KNX_TUNNELING; i++)
{
if(tunnels[i].IsConfig)
{
#ifdef KNX_LOG_TUNNELING
println("Found config Channel");
#endif
tun = &tunnels[i];
break;
}
}
}
if(tun == nullptr)
{
#ifdef KNX_LOG_TUNNELING
print("Found no Tunnel for IA: ");
println(frame.destinationAddress(), 16);
#endif
return;
}
sendFrameToTunnel(tun, frame);
}
void IpDataLinkLayer::dataConfirmationToTunnel(CemiFrame& frame)
{
if(frame.addressType() == AddressType::GroupAddress)
{
for(int i = 0; i < KNX_TUNNELING; i++)
if(tunnels[i].ChannelId != 0 && tunnels[i].IndividualAddress == frame.sourceAddress())
sendFrameToTunnel(&tunnels[i], frame);
//TODO check if source is from tunnel
return;
}
KnxIpTunnelConnection *tun = nullptr;
for(int i = 0; i < KNX_TUNNELING; i++)
{
if(tunnels[i].IndividualAddress == frame.destinationAddress())
continue;
if(tunnels[i].IndividualAddress == frame.sourceAddress())
{
tun = &tunnels[i];
break;
}
}
if(tun == nullptr)
{
for(int i = 0; i < KNX_TUNNELING; i++)
{
if(tunnels[i].IsConfig)
{
#ifdef KNX_LOG_TUNNELING
println("Found config Channel");
#endif
tun = &tunnels[i];
break;
}
}
}
if(tun == nullptr)
{
#ifdef KNX_LOG_TUNNELING
print("Found no Tunnel for IA: ");
println(frame.destinationAddress(), 16);
#endif
return;
}
sendFrameToTunnel(tun, frame);
}
void IpDataLinkLayer::dataIndicationToTunnel(CemiFrame& frame)
{
if(frame.addressType() == AddressType::GroupAddress)
{
for(int i = 0; i < KNX_TUNNELING; i++)
if(tunnels[i].ChannelId != 0 && tunnels[i].IndividualAddress != frame.sourceAddress())
sendFrameToTunnel(&tunnels[i], frame);
return;
}
KnxIpTunnelConnection *tun = nullptr;
for(int i = 0; i < KNX_TUNNELING; i++)
{
if(tunnels[i].ChannelId == 0 || tunnels[i].IndividualAddress == frame.sourceAddress())
continue;
if(tunnels[i].IndividualAddress == frame.destinationAddress())
{
tun = &tunnels[i];
break;
}
}
if(tun == nullptr)
{
for(int i = 0; i < KNX_TUNNELING; i++)
{
if(tunnels[i].IsConfig)
{
#ifdef KNX_LOG_TUNNELING
println("Found config Channel");
#endif
tun = &tunnels[i];
break;
}
}
}
if(tun == nullptr)
{
#ifdef KNX_LOG_TUNNELING
print("Found no Tunnel for IA: ");
println(frame.destinationAddress(), 16);
#endif
return;
}
sendFrameToTunnel(tun, frame);
}
void IpDataLinkLayer::sendFrameToTunnel(KnxIpTunnelConnection *tunnel, CemiFrame& frame)
{
#ifdef KNX_LOG_TUNNELING
print("Send to Channel: ");
println(tunnel->ChannelId, 16);
#endif
KnxIpTunnelingRequest req(frame);
req.connectionHeader().sequenceCounter(tunnel->SequenceCounter_S++);
req.connectionHeader().length(LEN_CH);
req.connectionHeader().channelId(tunnel->ChannelId);
if(frame.messageCode() != L_data_req && frame.messageCode() != L_data_con && frame.messageCode() != L_data_ind)
req.serviceTypeIdentifier(DeviceConfigurationRequest);
_platform.sendBytesUniCast(tunnel->IpAddress, tunnel->PortData, req.data(), req.totalLength());
}
bool IpDataLinkLayer::isTunnelAddress(uint16_t addr)
{
if(addr == 0)
return false; // 0.0.0 is not a valid tunnel address and is used as default value
for(int i = 0; i < KNX_TUNNELING; i++)
if(tunnels[i].IndividualAddress == addr)
return true;
return false;
}
bool IpDataLinkLayer::isSentToTunnel(uint16_t address, bool isGrpAddr)
{
if(isGrpAddr)
{
for(int i = 0; i < KNX_TUNNELING; i++)
if(tunnels[i].ChannelId != 0)
return true;
return false;
} else {
for(int i = 0; i < KNX_TUNNELING; i++)
if(tunnels[i].ChannelId != 0 && tunnels[i].IndividualAddress == address)
return true;
return false;
}
}
#endif
void IpDataLinkLayer::loop()
{
if (!_enabled)
return;
#ifdef KNX_TUNNELING
for(int i = 0; i < KNX_TUNNELING; i++)
{
if(tunnels[i].ChannelId != 0)
{
if(millis() - tunnels[i].lastHeartbeat > 120000)
{
#ifdef KNX_LOG_TUNNELING
print("Closed Tunnel 0x");
print(tunnels[i].ChannelId, 16);
println(" due to no heartbeat in 2 minutes");
#endif
KnxIpDisconnectRequest discReq;
discReq.channelId(tunnels[i].ChannelId);
discReq.hpaiCtrl().length(LEN_IPHPAI);
discReq.hpaiCtrl().code(IPV4_UDP);
discReq.hpaiCtrl().ipAddress(tunnels[i].IpAddress);
discReq.hpaiCtrl().ipPortNumber(tunnels[i].PortCtrl);
_platform.sendBytesUniCast(tunnels[i].IpAddress, tunnels[i].PortCtrl, discReq.data(), discReq.totalLength());
tunnels[i].Reset();
}
break;
}
}
#endif
uint8_t buffer[512];
int len = _platform.readBytesMultiCast(buffer, 512);
uint16_t remotePort = 0;
uint32_t remoteAddr = 0;
int len = _platform.readBytesMultiCast(buffer, 512, remoteAddr, remotePort);
if (len <= 0)
return;
@ -70,6 +314,7 @@ void IpDataLinkLayer::loop()
frameReceived(routingIndication.frame());
break;
}
case SearchRequest:
{
KnxIpSearchRequest searchRequest(buffer, len);
@ -85,17 +330,697 @@ void IpDataLinkLayer::loop()
}
case SearchRequestExt:
{
// FIXME, implement (not needed atm)
#if KNX_SERVICE_FAMILY_CORE >= 2
loopHandleSearchRequestExtended(buffer, len);
#endif
break;
}
default:
#ifdef KNX_TUNNELING
case ConnectRequest:
{
// print("Unhandled service identifier: ");
// println(code, HEX);
loopHandleConnectRequest(buffer, len, remoteAddr, remotePort);
break;
}
case ConnectionStateRequest:
{
loopHandleConnectionStateRequest(buffer, len);
break;
}
case DisconnectRequest:
{
loopHandleDisconnectRequest(buffer, len);
break;
}
case DescriptionRequest:
{
loopHandleDescriptionRequest(buffer, len);
break;
}
case DeviceConfigurationRequest:
{
loopHandleDeviceConfigurationRequest(buffer, len);
break;
}
case TunnelingRequest:
{
loopHandleTunnelingRequest(buffer, len);
return;
}
case DeviceConfigurationAck:
{
//TOOD nothing to do now
//println("got Ack");
break;
}
case TunnelingAck:
{
//TOOD nothing to do now
//println("got Ack");
break;
}
#endif
default:
print("Unhandled service identifier: ");
println(code, HEX);
break;
}
}
#if KNX_SERVICE_FAMILY_CORE >= 2
void IpDataLinkLayer::loopHandleSearchRequestExtended(uint8_t* buffer, uint16_t length)
{
KnxIpSearchRequestExtended searchRequest(buffer, length);
if(searchRequest.srpByProgMode)
{
println("srpByProgMode");
if(!knx.progMode()) return;
}
if(searchRequest.srpByMacAddr)
{
println("srpByMacAddr");
const uint8_t *x = _ipParameters.propertyData(PID_MAC_ADDRESS);
for(int i = 0; i<6;i++)
if(searchRequest.srpMacAddr[i] != x[i])
return;
}
#define LEN_SERVICE_FAMILIES 2
#if MASK_VERSION == 0x091A
#ifdef KNX_TUNNELING
#define LEN_SERVICE_DIB (2 + 4 * LEN_SERVICE_FAMILIES)
#else
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
#endif
#else
#ifdef KNX_TUNNELING
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
#else
#define LEN_SERVICE_DIB (2 + 2 * LEN_SERVICE_FAMILIES)
#endif
#endif
//defaults: "Device Information DIB", "Extended Device Information DIB" and "Supported Services DIB".
int dibLength = LEN_DEVICE_INFORMATION_DIB + LEN_SERVICE_DIB + LEN_EXTENDED_DEVICE_INFORMATION_DIB;
if(searchRequest.srpByService)
{
println("srpByService");
uint8_t length = searchRequest.srpServiceFamilies[0];
uint8_t *currentPos = searchRequest.srpServiceFamilies + 2;
for(int i = 0; i < (length-2)/2; i++)
{
uint8_t serviceFamily = (currentPos + i*2)[0];
uint8_t version = (currentPos + i*2)[1];
switch(serviceFamily)
{
case Core:
if(version > KNX_SERVICE_FAMILY_CORE) return;
break;
case DeviceManagement:
if(version > KNX_SERVICE_FAMILY_DEVICE_MANAGEMENT) return;
break;
case Tunnelling:
if(version > KNX_SERVICE_FAMILY_TUNNELING) return;
break;
case Routing:
if(version > KNX_SERVICE_FAMILY_ROUTING) return;
break;
}
}
}
if(searchRequest.srpRequestDIBs)
{
println("srpRequestDIBs");
if(searchRequest.requestedDIB(IP_CONFIG))
dibLength += LEN_IP_CONFIG_DIB; //16
if(searchRequest.requestedDIB(IP_CUR_CONFIG))
dibLength += LEN_IP_CURRENT_CONFIG_DIB; //20
if(searchRequest.requestedDIB(KNX_ADDRESSES))
{uint16_t length = 0;
_ipParameters.readPropertyLength(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES, length);
dibLength += 4 + length*2;
}
if(searchRequest.requestedDIB(MANUFACTURER_DATA))
dibLength += 0; //4 + n
if(searchRequest.requestedDIB(TUNNELING_INFO))
{
uint16_t length = 0;
_ipParameters.readPropertyLength(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES, length);
dibLength += 4 + length*4;
}
}
KnxIpSearchResponseExtended searchResponse(_ipParameters, _deviceObject, dibLength);
searchResponse.setDeviceInfo(_ipParameters, _deviceObject); //DescriptionTypeCode::DeviceInfo 1
searchResponse.setSupportedServices(); //DescriptionTypeCode::SUPP_SVC_FAMILIES 2
searchResponse.setExtendedDeviceInfo(); //DescriptionTypeCode::EXTENDED_DEVICE_INFO 8
if(searchRequest.srpRequestDIBs)
{
if(searchRequest.requestedDIB(IP_CONFIG))
searchResponse.setIpConfig(_ipParameters);
if(searchRequest.requestedDIB(IP_CUR_CONFIG))
searchResponse.setIpCurrentConfig(_ipParameters);
if(searchRequest.requestedDIB(KNX_ADDRESSES))
searchResponse.setKnxAddresses(_ipParameters, _deviceObject);
if(searchRequest.requestedDIB(MANUFACTURER_DATA))
{
//println("requested MANUFACTURER_DATA but not implemented");
}
if(searchRequest.requestedDIB(TUNNELING_INFO))
searchResponse.setTunnelingInfo(_ipParameters, _deviceObject, tunnels);
}
if(searchResponse.totalLength() > 150)
{
println("skipped response cause length is not plausible");
return;
}
_platform.sendBytesUniCast(searchRequest.hpai().ipAddress(), searchRequest.hpai().ipPortNumber(), searchResponse.data(), searchResponse.totalLength());
}
#endif
#ifdef KNX_TUNNELING
void IpDataLinkLayer::loopHandleConnectRequest(uint8_t* buffer, uint16_t length, uint32_t& src_addr, uint16_t& src_port)
{
KnxIpConnectRequest connRequest(buffer, length);
#ifdef KNX_LOG_TUNNELING
println("Got Connect Request!");
switch(connRequest.cri().type())
{
case DEVICE_MGMT_CONNECTION:
println("Device Management Connection");
break;
case TUNNEL_CONNECTION:
println("Tunnel Connection");
break;
case REMLOG_CONNECTION:
println("RemLog Connection");
break;
case REMCONF_CONNECTION:
println("RemConf Connection");
break;
case OBJSVR_CONNECTION:
println("ObjectServer Connection");
break;
}
print("Data Endpoint: ");
uint32_t ip = connRequest.hpaiData().ipAddress();
print(ip >> 24);
print(".");
print((ip >> 16) & 0xFF);
print(".");
print((ip >> 8) & 0xFF);
print(".");
print(ip & 0xFF);
print(":");
println(connRequest.hpaiData().ipPortNumber());
print("Ctrl Endpoint: ");
ip = connRequest.hpaiCtrl().ipAddress();
print(ip >> 24);
print(".");
print((ip >> 16) & 0xFF);
print(".");
print((ip >> 8) & 0xFF);
print(".");
print(ip & 0xFF);
print(":");
println(connRequest.hpaiCtrl().ipPortNumber());
#endif
//We only support 0x03 and 0x04!
if(connRequest.cri().type() != TUNNEL_CONNECTION && connRequest.cri().type() != DEVICE_MGMT_CONNECTION)
{
#ifdef KNX_LOG_TUNNELING
println("Only Tunnel/DeviceMgmt Connection ist supported!");
#endif
KnxIpConnectResponse connRes(0x00, E_CONNECTION_TYPE);
_platform.sendBytesUniCast(connRequest.hpaiCtrl().ipAddress(), connRequest.hpaiCtrl().ipPortNumber(), connRes.data(), connRes.totalLength());
return;
}
if(connRequest.cri().type() == TUNNEL_CONNECTION && connRequest.cri().layer() != 0x02) //LinkLayer
{
//We only support 0x02!
#ifdef KNX_LOG_TUNNELING
println("Only LinkLayer ist supported!");
#endif
KnxIpConnectResponse connRes(0x00, E_TUNNELING_LAYER);
_platform.sendBytesUniCast(connRequest.hpaiCtrl().ipAddress(), connRequest.hpaiCtrl().ipPortNumber(), connRes.data(), connRes.totalLength());
return;
}
// data preparation
uint32_t srcIP = connRequest.hpaiCtrl().ipAddress()? connRequest.hpaiCtrl().ipAddress() : src_addr;
uint16_t srcPort = connRequest.hpaiCtrl().ipPortNumber()? connRequest.hpaiCtrl().ipPortNumber() : src_port;
// read current elements in PID_ADDITIONAL_INDIVIDUAL_ADDRESSES
uint16_t propCount = 0;
_ipParameters.readPropertyLength(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES, propCount);
const uint8_t *addresses;
if(propCount == KNX_TUNNELING)
{
addresses = _ipParameters.propertyData(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES);
}
else // no tunnel PA configured, that means device is unconfigured and has 15.15.0
{
uint8_t addrbuffer[KNX_TUNNELING*2];
addresses = (uint8_t*)addrbuffer;
for(int i = 0; i < KNX_TUNNELING; i++)
{
addrbuffer[i*2+1] = i+1;
addrbuffer[i*2] = _deviceObject.individualAddress() / 0x0100;
}
uint8_t count = KNX_TUNNELING;
_ipParameters.writeProperty(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES, 1, addrbuffer, count);
#ifdef KNX_LOG_TUNNELING
println("no Tunnel-PAs configured, using own subnet");
#endif
}
_ipParameters.readPropertyLength(PID_CUSTOM_RESERVED_TUNNELS_CTRL, propCount);
const uint8_t *tunCtrlBytes = nullptr;
if(propCount == KNX_TUNNELING)
tunCtrlBytes = _ipParameters.propertyData(PID_CUSTOM_RESERVED_TUNNELS_CTRL);
_ipParameters.readPropertyLength(PID_CUSTOM_RESERVED_TUNNELS_IP, propCount);
const uint8_t *tunCtrlIp = nullptr;
if(propCount == KNX_TUNNELING)
tunCtrlIp = _ipParameters.propertyData(PID_CUSTOM_RESERVED_TUNNELS_IP);
bool resTunActive = (tunCtrlBytes && tunCtrlIp);
#ifdef KNX_LOG_TUNNELING
if(resTunActive) println("Reserved Tunnel Feature active");
if(tunCtrlBytes)
printHex("tunCtrlBytes", tunCtrlBytes, KNX_TUNNELING);
if(tunCtrlIp)
printHex("tunCtrlIp", tunCtrlIp, KNX_TUNNELING*4);
#endif
// check if there is a reserved tunnel for the source
int firstFreeTunnel = -1;
int firstResAndFreeTunnel = -1;
int firstResAndOccTunnel = -1;
bool tunnelResActive[KNX_TUNNELING];
uint8_t tunnelResOptions[KNX_TUNNELING];
for(int i = 0; i < KNX_TUNNELING; i++)
{
if(resTunActive)
{
tunnelResActive[i] = *(tunCtrlBytes+i) & 0x80;
tunnelResOptions[i] = (*(tunCtrlBytes+i) & 0x60) >> 5;
}
if(tunnelResActive[i]) // tunnel reserve feature active for this tunnel
{
#ifdef KNX_LOG_TUNNELING
print("tunnel reserve feature active for this tunnel: ");
print(tunnelResActive[i]);
print(" options: ");
println(tunnelResOptions[i]);
#endif
uint32_t rIP = 0;
popInt(rIP, tunCtrlIp+4*i);
if(srcIP == rIP && connRequest.cri().type() == TUNNEL_CONNECTION)
{
// reserved tunnel for this ip found
if(tunnels[i].ChannelId == 0) // check if it is free
{
if(firstResAndFreeTunnel < 0)
firstResAndFreeTunnel = i;
}
else
{
if(firstResAndOccTunnel < 0)
firstResAndOccTunnel = i;
}
}
}
else
{
if(tunnels[i].ChannelId == 0 && firstFreeTunnel < 0)
firstFreeTunnel = i;
}
}
#ifdef KNX_LOG_TUNNELING
print("firstFreeTunnel: ");
print(firstFreeTunnel);
print(" firstResAndFreeTunnel: ");
print(firstResAndFreeTunnel);
print(" firstResAndOccTunnel: ");
println(firstResAndOccTunnel);
#endif
uint8_t tunIdx = 0xff;
if(resTunActive & (firstResAndFreeTunnel >= 0 || firstResAndOccTunnel >= 0)) // tunnel reserve feature active (for this src)
{
if(firstResAndFreeTunnel >= 0)
{
tunIdx = firstResAndFreeTunnel;
}
else if(firstResAndOccTunnel >= 0)
{
if(tunnelResOptions[firstResAndOccTunnel] == 1) // decline req
{
; // do nothing => decline
}
else if(tunnelResOptions[firstResAndOccTunnel] == 2) // close current tunnel connection on this tunnel and assign to this request
{
KnxIpDisconnectRequest discReq;
discReq.channelId(tunnels[firstResAndOccTunnel].ChannelId);
discReq.hpaiCtrl().length(LEN_IPHPAI);
discReq.hpaiCtrl().code(IPV4_UDP);
discReq.hpaiCtrl().ipAddress(tunnels[firstResAndOccTunnel].IpAddress);
discReq.hpaiCtrl().ipPortNumber(tunnels[firstResAndOccTunnel].PortCtrl);
_platform.sendBytesUniCast(tunnels[firstResAndOccTunnel].IpAddress, tunnels[firstResAndOccTunnel].PortCtrl, discReq.data(), discReq.totalLength());
tunnels[firstResAndOccTunnel].Reset();
tunIdx = firstResAndOccTunnel;
}
else if(tunnelResOptions[firstResAndOccTunnel] == 3) // use the first unreserved tunnel (if one)
{
if(firstFreeTunnel >= 0)
tunIdx = firstFreeTunnel;
else
; // do nothing => decline
}
//else
// should not happen
// do nothing => decline
}
//else
// should not happen
// do nothing => decline
}
else
{
if(firstFreeTunnel >= 0)
tunIdx = firstFreeTunnel;
//else
// do nothing => decline
}
KnxIpTunnelConnection *tun = nullptr;
if(tunIdx != 0xFF)
{
tun = &tunnels[tunIdx];
uint16_t tunPa = 0;
popWord(tunPa, addresses + (tunIdx*2));
//check if this PA is in use (should not happen, only when there is one pa wrongly assigned to more then one tunnel)
for(int x = 0; x < KNX_TUNNELING; x++)
if(tunnels[x].IndividualAddress == tunPa)
{
#ifdef KNX_LOG_TUNNELING
println("cannot use tunnel because PA is already in use");
#endif
tunIdx = 0xFF;
tun = nullptr;
break;
}
tun->IndividualAddress = tunPa;
}
if(tun == nullptr)
{
println("no free tunnel availible");
KnxIpConnectResponse connRes(0x00, E_NO_MORE_CONNECTIONS);
_platform.sendBytesUniCast(connRequest.hpaiCtrl().ipAddress(), connRequest.hpaiCtrl().ipPortNumber(), connRes.data(), connRes.totalLength());
return;
}
if(connRequest.cri().type() == DEVICE_MGMT_CONNECTION)
tun->IsConfig = true;
// the channel ID shall be unique on this tunnel server. catch the rare case of a double channel ID
bool channelIdInUse;
do
{
_lastChannelId++;
channelIdInUse = false;
for(int x = 0; x < KNX_TUNNELING; x++)
if(tunnels[x].ChannelId == _lastChannelId)
channelIdInUse = true;
}
while(channelIdInUse);
tun->ChannelId = _lastChannelId;
tun->lastHeartbeat = millis();
if(_lastChannelId == 255)
_lastChannelId = 0;
tun->IpAddress = srcIP;
tun->PortData = srcPort;
tun->PortCtrl = connRequest.hpaiCtrl().ipPortNumber()?connRequest.hpaiCtrl().ipPortNumber():srcPort;
print("New Tunnel-Connection[");
print(tunIdx);
print("], Channel: 0x");
print(tun->ChannelId, 16);
print(" PA: ");
print(tun->IndividualAddress >> 12);
print(".");
print((tun->IndividualAddress >> 8) & 0xF);
print(".");
print(tun->IndividualAddress & 0xFF);
print(" with ");
print(tun->IpAddress >> 24);
print(".");
print((tun->IpAddress >> 16) & 0xFF);
print(".");
print((tun->IpAddress >> 8) & 0xFF);
print(".");
print(tun->IpAddress & 0xFF);
print(":");
print(tun->PortData);
if(tun->PortData != tun->PortCtrl)
{
print(" (Ctrlport: ");
print(tun->PortCtrl);
print(")");
}
if(tun->IsConfig)
{
print(" (Config-Channel)");
}
println();
KnxIpConnectResponse connRes(_ipParameters, tun->IndividualAddress, 3671, tun->ChannelId, connRequest.cri().type());
_platform.sendBytesUniCast(tun->IpAddress, tun->PortCtrl, connRes.data(), connRes.totalLength());
}
void IpDataLinkLayer::loopHandleConnectionStateRequest(uint8_t* buffer, uint16_t length)
{
KnxIpStateRequest stateRequest(buffer, length);
KnxIpTunnelConnection *tun = nullptr;
for(int i = 0; i < KNX_TUNNELING; i++)
{
if(tunnels[i].ChannelId == stateRequest.channelId())
{
tun = &tunnels[i];
break;
}
}
if(tun == nullptr)
{
#ifdef KNX_LOG_TUNNELING
print("Channel ID nicht gefunden: ");
println(stateRequest.channelId());
#endif
KnxIpStateResponse stateRes(0x00, E_CONNECTION_ID);
_platform.sendBytesUniCast(stateRequest.hpaiCtrl().ipAddress(), stateRequest.hpaiCtrl().ipPortNumber(), stateRes.data(), stateRes.totalLength());
return;
}
//TODO check knx connection!
//if no connection return E_KNX_CONNECTION
//TODO check when to send E_DATA_CONNECTION
tun->lastHeartbeat = millis();
KnxIpStateResponse stateRes(tun->ChannelId, E_NO_ERROR);
_platform.sendBytesUniCast(stateRequest.hpaiCtrl().ipAddress(), stateRequest.hpaiCtrl().ipPortNumber(), stateRes.data(), stateRes.totalLength());
}
void IpDataLinkLayer::loopHandleDisconnectRequest(uint8_t* buffer, uint16_t length)
{
KnxIpDisconnectRequest discReq(buffer, length);
#ifdef KNX_LOG_TUNNELING
print(">>> Disconnect Channel ID: ");
println(discReq.channelId());
#endif
KnxIpTunnelConnection *tun = nullptr;
for(int i = 0; i < KNX_TUNNELING; i++)
{
if(tunnels[i].ChannelId == discReq.channelId())
{
tun = &tunnels[i];
break;
}
}
if(tun == nullptr)
{
#ifdef KNX_LOG_TUNNELING
print("Channel ID nicht gefunden: ");
println(discReq.channelId());
#endif
KnxIpDisconnectResponse discRes(0x00, E_CONNECTION_ID);
_platform.sendBytesUniCast(discReq.hpaiCtrl().ipAddress(), discReq.hpaiCtrl().ipPortNumber(), discRes.data(), discRes.totalLength());
return;
}
KnxIpDisconnectResponse discRes(tun->ChannelId, E_NO_ERROR);
_platform.sendBytesUniCast(discReq.hpaiCtrl().ipAddress(), discReq.hpaiCtrl().ipPortNumber(), discRes.data(), discRes.totalLength());
tun->Reset();
}
void IpDataLinkLayer::loopHandleDescriptionRequest(uint8_t* buffer, uint16_t length)
{
KnxIpDescriptionRequest descReq(buffer, length);
KnxIpDescriptionResponse descRes(_ipParameters, _deviceObject);
_platform.sendBytesUniCast(descReq.hpaiCtrl().ipAddress(), descReq.hpaiCtrl().ipPortNumber(), descRes.data(), descRes.totalLength());
}
void IpDataLinkLayer::loopHandleDeviceConfigurationRequest(uint8_t* buffer, uint16_t length)
{
KnxIpConfigRequest confReq(buffer, length);
KnxIpTunnelConnection *tun = nullptr;
for(int i = 0; i < KNX_TUNNELING; i++)
{
if(tunnels[i].ChannelId == confReq.connectionHeader().channelId())
{
tun = &tunnels[i];
break;
}
}
if(tun == nullptr)
{
print("Channel ID nicht gefunden: ");
println(confReq.connectionHeader().channelId());
KnxIpStateResponse stateRes(0x00, E_CONNECTION_ID);
_platform.sendBytesUniCast(0, 0, stateRes.data(), stateRes.totalLength());
return;
}
KnxIpTunnelingAck tunnAck;
tunnAck.serviceTypeIdentifier(DeviceConfigurationAck);
tunnAck.connectionHeader().length(4);
tunnAck.connectionHeader().channelId(tun->ChannelId);
tunnAck.connectionHeader().sequenceCounter(confReq.connectionHeader().sequenceCounter());
tunnAck.connectionHeader().status(E_NO_ERROR);
_platform.sendBytesUniCast(tun->IpAddress, tun->PortData, tunnAck.data(), tunnAck.totalLength());
tun->lastHeartbeat = millis();
_cemiServer->frameReceived(confReq.frame());
}
void IpDataLinkLayer::loopHandleTunnelingRequest(uint8_t* buffer, uint16_t length)
{
KnxIpTunnelingRequest tunnReq(buffer, length);
KnxIpTunnelConnection *tun = nullptr;
for(int i = 0; i < KNX_TUNNELING; i++)
{
if(tunnels[i].ChannelId == tunnReq.connectionHeader().channelId())
{
tun = &tunnels[i];
break;
}
}
if(tun == nullptr)
{
#ifdef KNX_LOG_TUNNELING
print("Channel ID nicht gefunden: ");
println(tunnReq.connectionHeader().channelId());
#endif
KnxIpStateResponse stateRes(0x00, E_CONNECTION_ID);
_platform.sendBytesUniCast(0, 0, stateRes.data(), stateRes.totalLength());
return;
}
uint8_t sequence = tunnReq.connectionHeader().sequenceCounter();
if(sequence == tun->SequenceCounter_R)
{
#ifdef KNX_LOG_TUNNELING
print("Received SequenceCounter again: ");
println(tunnReq.connectionHeader().sequenceCounter());
#endif
//we already got this one
//so just ack it
KnxIpTunnelingAck tunnAck;
tunnAck.connectionHeader().length(4);
tunnAck.connectionHeader().channelId(tun->ChannelId);
tunnAck.connectionHeader().sequenceCounter(tunnReq.connectionHeader().sequenceCounter());
tunnAck.connectionHeader().status(E_NO_ERROR);
_platform.sendBytesUniCast(tun->IpAddress, tun->PortData, tunnAck.data(), tunnAck.totalLength());
return;
} else if((uint8_t)(sequence - 1) != tun->SequenceCounter_R) {
#ifdef KNX_LOG_TUNNELING
print("Wrong SequenceCounter: got ");
print(tunnReq.connectionHeader().sequenceCounter());
print(" expected ");
println((uint8_t)(tun->SequenceCounter_R + 1));
#endif
//Dont handle it
return;
}
KnxIpTunnelingAck tunnAck;
tunnAck.connectionHeader().length(4);
tunnAck.connectionHeader().channelId(tun->ChannelId);
tunnAck.connectionHeader().sequenceCounter(tunnReq.connectionHeader().sequenceCounter());
tunnAck.connectionHeader().status(E_NO_ERROR);
_platform.sendBytesUniCast(tun->IpAddress, tun->PortData, tunnAck.data(), tunnAck.totalLength());
tun->SequenceCounter_R = tunnReq.connectionHeader().sequenceCounter();
if(tunnReq.frame().sourceAddress() == 0)
tunnReq.frame().sourceAddress(tun->IndividualAddress);
_cemiServer->frameReceived(tunnReq.frame());
}
#endif
void IpDataLinkLayer::enabled(bool value)
{
// _print("own address: ");
@ -143,7 +1068,7 @@ bool IpDataLinkLayer::isSendLimitReached()
uint32_t timeBaseDiff = _frameCountTimeBase - curTime;
if(timeBaseDiff > 10)
timeBaseDiff = 10;
for(int i = 0; i < timeBaseDiff ; i++)
for(uint32_t i = 0; i < timeBaseDiff ; i++)
{
_frameCountBase++;
_frameCountBase = _frameCountBase % 10;

View File

@ -6,6 +6,8 @@
#include <stdint.h>
#include "data_link_layer.h"
#include "ip_parameter_object.h"
#include "knx_ip_tunnel_connection.h"
#include "service_families.h"
class IpDataLinkLayer : public DataLinkLayer
{
@ -13,12 +15,19 @@ class IpDataLinkLayer : public DataLinkLayer
public:
IpDataLinkLayer(DeviceObject& devObj, IpParameterObject& ipParam, NetworkLayerEntity& netLayerEntity,
Platform& platform, DataLinkLayerCallbacks* dllcb = nullptr);
Platform& platform, BusAccessUnit& busAccessUnit, DataLinkLayerCallbacks* dllcb = nullptr);
void loop();
void enabled(bool value);
bool enabled() const;
DptMedium mediumType() const override;
#ifdef KNX_TUNNELING
void dataRequestToTunnel(CemiFrame& frame) override;
void dataConfirmationToTunnel(CemiFrame& frame) override;
void dataIndicationToTunnel(CemiFrame& frame) override;
bool isTunnelAddress(uint16_t addr) override;
bool isSentToTunnel(uint16_t address, bool isGrpAddr);
#endif
private:
bool _enabled = false;
@ -26,10 +35,26 @@ class IpDataLinkLayer : public DataLinkLayer
uint8_t _frameCountBase = 0;
uint32_t _frameCountTimeBase = 0;
bool sendFrame(CemiFrame& frame);
#ifdef KNX_TUNNELING
void sendFrameToTunnel(KnxIpTunnelConnection *tunnel, CemiFrame& frame);
void loopHandleConnectRequest(uint8_t* buffer, uint16_t length, uint32_t& src_addr, uint16_t& src_port);
void loopHandleConnectionStateRequest(uint8_t* buffer, uint16_t length);
void loopHandleDisconnectRequest(uint8_t* buffer, uint16_t length);
void loopHandleDescriptionRequest(uint8_t* buffer, uint16_t length);
void loopHandleDeviceConfigurationRequest(uint8_t* buffer, uint16_t length);
void loopHandleTunnelingRequest(uint8_t* buffer, uint16_t length);
#endif
#if KNX_SERVICE_FAMILY_CORE >= 2
void loopHandleSearchRequestExtended(uint8_t* buffer, uint16_t length);
#endif
bool sendBytes(uint8_t* buffer, uint16_t length);
bool isSendLimitReached();
IpParameterObject& _ipParameters;
DataLinkLayerCallbacks* _dllcb;
};
#ifdef KNX_TUNNELING
KnxIpTunnelConnection tunnels[KNX_TUNNELING];
uint8_t _lastChannelId = 1;
#endif
};
#endif

View File

@ -12,6 +12,7 @@ enum HostProtocolCode : uint8_t
#ifdef USE_IP
#define LEN_IPHPAI 8
#define LEN_CRD 4
class IpHostProtocolAddressInformation
{

View File

@ -34,6 +34,11 @@ IpParameterObject::IpParameterObject(DeviceObject& deviceObject, Platform& platf
io->_deviceObject.individualAddress(getWord(data));
return 1;
}),
#ifdef KNX_TUNNELING
new DataProperty(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES, true, PDT_UNSIGNED_INT, KNX_TUNNELING, ReadLv3 | WriteLv3),
new DataProperty(PID_CUSTOM_RESERVED_TUNNELS_CTRL, true, PDT_UNSIGNED_CHAR, KNX_TUNNELING, ReadLv3 | WriteLv3), // custom propertiy to control the stacks behaviour for reserverd tunnels, not in Spec (PID >= 200)
new DataProperty(PID_CUSTOM_RESERVED_TUNNELS_IP, true, PDT_UNSIGNED_LONG, KNX_TUNNELING, ReadLv3 | WriteLv3), // custom propertiy to control the stacks behaviour for reserverd tunnels, not in Spec (PID >= 200)
#endif
new DataProperty(PID_CURRENT_IP_ASSIGNMENT_METHOD, false, PDT_UNSIGNED_CHAR, 0, ReadLv3 | WriteLv3),
new DataProperty(PID_IP_ASSIGNMENT_METHOD, true, PDT_UNSIGNED_CHAR, 1, ReadLv3 | WriteLv3),
new DataProperty(PID_IP_CAPABILITIES, true, PDT_BITSET8, 0, ReadLv3 | WriteLv1), // must be set by application due to capabilities of the used ip stack

48
src/knx/knx_ip_ch.cpp Normal file
View File

@ -0,0 +1,48 @@
#include "knx_ip_ch.h"
#ifdef USE_IP
KnxIpCH::KnxIpCH(uint8_t* data) : _data(data)
{}
KnxIpCH::~KnxIpCH()
{}
uint8_t KnxIpCH::length() const
{
return *_data;
}
void KnxIpCH::length(uint8_t value)
{
*_data = value;
}
void KnxIpCH::channelId(uint8_t value)
{
_data[1] = value;
}
uint8_t KnxIpCH::channelId() const
{
return _data[1];
}
void KnxIpCH::sequenceCounter(uint8_t value)
{
_data[2] = value;
}
uint8_t KnxIpCH::sequenceCounter() const
{
return _data[2];
}
void KnxIpCH::status(uint8_t value)
{
_data[3] = value;
}
uint8_t KnxIpCH::status() const
{
return _data[3];
}
#endif

28
src/knx/knx_ip_ch.h Normal file
View File

@ -0,0 +1,28 @@
#pragma once
#include <cstdint>
#include "config.h"
#ifdef USE_IP
#define LEN_CH 4
// Connection Header
class KnxIpCH
{
public:
KnxIpCH(uint8_t* data);
virtual ~KnxIpCH();
void channelId(uint8_t channelId);
uint8_t channelId() const;
void sequenceCounter(uint8_t sequenceCounter);
uint8_t sequenceCounter() const;
void status(uint8_t status);
uint8_t status() const;
void length(uint8_t value);
uint8_t length() const;
protected:
uint8_t* _data = 0;
};
#endif

View File

@ -0,0 +1,91 @@
#include "knx_ip_config_dib.h"
#ifdef USE_IP
KnxIpConfigDIB::KnxIpConfigDIB(uint8_t* data, bool isCurrent) : KnxIpDIB(data)
{
_isCurrent = isCurrent;
}
uint32_t KnxIpConfigDIB::address()
{
uint32_t addr = 0;
popInt(addr, _data + 2);
return addr;
}
void KnxIpConfigDIB::address(uint32_t addr)
{
pushInt(addr, _data + 2);
}
uint32_t KnxIpConfigDIB::subnet()
{
uint32_t addr = 0;
popInt(addr, _data + 6);
return addr;
}
void KnxIpConfigDIB::subnet(uint32_t addr)
{
pushInt(addr, _data + 6);
}
uint32_t KnxIpConfigDIB::gateway()
{
uint32_t addr = 0;
popInt(addr, _data + 10);
return addr;
}
void KnxIpConfigDIB::gateway(uint32_t addr)
{
pushInt(addr, _data + 10);
}
uint32_t KnxIpConfigDIB::dhcp()
{
if(!_isCurrent) return 0;
uint32_t addr = 0;
popInt(addr, _data + 14);
return addr;
}
void KnxIpConfigDIB::dhcp(uint32_t addr)
{
if(!_isCurrent) return;
pushInt(addr, _data + 14);
}
uint8_t KnxIpConfigDIB::info1()
{
if(_isCurrent)
return _data[14];
else
return _data[18];
}
void KnxIpConfigDIB::info1(uint8_t addr)
{
if(_isCurrent)
_data[14] = addr;
else
_data[18] = addr;
}
uint8_t KnxIpConfigDIB::info2()
{
if(_isCurrent)
return _data[15];
else
return _data[19];
}
void KnxIpConfigDIB::info2(uint8_t addr)
{
if(_isCurrent)
_data[15] = addr;
else
_data[19] = addr;
}
#endif

View File

@ -0,0 +1,28 @@
#pragma once
#include "knx_ip_dib.h"
#include "bits.h"
#ifdef USE_IP
#define LEN_IP_CONFIG_DIB 16
#define LEN_IP_CURRENT_CONFIG_DIB 20
class KnxIpConfigDIB : public KnxIpDIB
{
public:
KnxIpConfigDIB(uint8_t* data, bool isCurrent = false);
uint32_t address();
void address(uint32_t addr);
uint32_t subnet();
void subnet(uint32_t addr);
uint32_t gateway();
void gateway(uint32_t addr);
uint32_t dhcp();
void dhcp(uint32_t addr);
uint8_t info1();
void info1(uint8_t addr);
uint8_t info2();
void info2(uint8_t addr);
private:
bool _isCurrent = false;
};
#endif

View File

@ -0,0 +1,17 @@
#include "knx_ip_config_request.h"
#ifdef USE_IP
KnxIpConfigRequest::KnxIpConfigRequest(uint8_t* data, uint16_t length)
: KnxIpFrame(data, length), _ch(data + LEN_KNXIP_HEADER), _frame(data + LEN_KNXIP_HEADER + LEN_CH, length - LEN_KNXIP_HEADER - LEN_CH)
{
}
CemiFrame& KnxIpConfigRequest::frame()
{
return _frame;
}
KnxIpCH& KnxIpConfigRequest::connectionHeader()
{
return _ch;
}
#endif

View File

@ -0,0 +1,17 @@
#pragma once
#include "knx_ip_frame.h"
#include "knx_ip_ch.h"
#include "ip_host_protocol_address_information.h"
#ifdef USE_IP
class KnxIpConfigRequest : public KnxIpFrame
{
public:
KnxIpConfigRequest(uint8_t* data, uint16_t length);
CemiFrame& frame();
KnxIpCH& connectionHeader();
private:
CemiFrame _frame;
KnxIpCH _ch;
};
#endif

View File

@ -0,0 +1,21 @@
#include "knx_ip_connect_request.h"
#ifdef USE_IP
KnxIpConnectRequest::KnxIpConnectRequest(uint8_t* data, uint16_t length)
: KnxIpFrame(data, length), _hpaiCtrl(data + LEN_KNXIP_HEADER), _hpaiData(data + LEN_KNXIP_HEADER + LEN_IPHPAI), _cri(data + LEN_KNXIP_HEADER + 2*LEN_IPHPAI)
{
}
IpHostProtocolAddressInformation& KnxIpConnectRequest::hpaiCtrl()
{
return _hpaiCtrl;
}
IpHostProtocolAddressInformation& KnxIpConnectRequest::hpaiData()
{
return _hpaiData;
}
KnxIpCRI& KnxIpConnectRequest::cri()
{
return _cri;
}
#endif

View File

@ -0,0 +1,19 @@
#pragma once
#include "knx_ip_frame.h"
#include "knx_ip_cri.h"
#include "ip_host_protocol_address_information.h"
#ifdef USE_IP
class KnxIpConnectRequest : public KnxIpFrame
{
public:
KnxIpConnectRequest(uint8_t* data, uint16_t length);
IpHostProtocolAddressInformation& hpaiCtrl();
IpHostProtocolAddressInformation& hpaiData();
KnxIpCRI& cri();
private:
IpHostProtocolAddressInformation _hpaiCtrl;
IpHostProtocolAddressInformation _hpaiData;
KnxIpCRI _cri;
};
#endif

View File

@ -0,0 +1,45 @@
#include "knx_ip_connect_response.h"
#ifdef USE_IP
KnxIpConnectResponse::KnxIpConnectResponse(IpParameterObject& parameters, uint16_t address, uint16_t port, uint8_t channel, uint8_t type)
: KnxIpFrame(LEN_KNXIP_HEADER + 1 /*Channel*/ + 1 /*Status*/ + LEN_IPHPAI + ((type == 4) ? 4 : 2)),
_controlEndpoint(_data + LEN_KNXIP_HEADER + 1 /*Channel*/ + 1 /*Status*/),
_crd(_data + LEN_KNXIP_HEADER + 1 /*Channel*/ + 1 /*Status*/ + LEN_IPHPAI)
{
serviceTypeIdentifier(ConnectResponse);
_data[LEN_KNXIP_HEADER] = channel;
_controlEndpoint.length(LEN_IPHPAI);
_controlEndpoint.code(IPV4_UDP);
_controlEndpoint.ipAddress(parameters.propertyValue<uint32_t>(PID_CURRENT_IP_ADDRESS));
_controlEndpoint.ipPortNumber(KNXIP_MULTICAST_PORT);
_crd.length((type == 4) ? 4 : 2); //TunnelConnectionResponse length = 4; ConfigConnectionResponse length = 2;
_crd.type(type);
_crd.address(address);
}
KnxIpConnectResponse::KnxIpConnectResponse(uint8_t channel, uint8_t errorCode)
: KnxIpFrame(LEN_KNXIP_HEADER + 1 /*Channel*/ + 1 /*Status*/),
_controlEndpoint(nullptr),
_crd(nullptr)
{
serviceTypeIdentifier(ConnectResponse);
_data[LEN_KNXIP_HEADER] = channel;
_data[LEN_KNXIP_HEADER + 1] = errorCode;
}
IpHostProtocolAddressInformation& KnxIpConnectResponse::controlEndpoint()
{
return _controlEndpoint;
}
KnxIpCRD& KnxIpConnectResponse::crd()
{
return _crd;
}
#endif

View File

@ -0,0 +1,45 @@
#pragma once
#include "knx_ip_frame.h"
#include "knx_ip_crd.h"
#include "ip_host_protocol_address_information.h"
#include "knx_ip_device_information_dib.h"
#include "knx_ip_supported_service_dib.h"
#include "ip_parameter_object.h"
#ifdef USE_IP
enum KnxIpConnectionRequestErrorCodes
{
E_NO_ERROR = 0,
E_HOST_PROTOCOL_TYPE = 0x01,
E_VERSION_NOT_SUPPORTED = 0x02,
E_SEQUENCE_NUMBER = 0x04,
E_ERROR = 0x0F,
E_CONNECTION_ID = 0x21,
E_CONNECTION_TYPE = 0x22,
E_CONNECTION_OPTION = 0x23,
E_NO_MORE_CONNECTIONS = 0x24,
E_DATA_CONNECTION = 0x26,
E_KNX_CONNECTION = 0x27,
E_AUTHORISATION_ERROR = 0x28,
E_TUNNELING_LAYER = 0x29,
E_NO_TUNNELLING_ADDRESS = 0x2D,
E_CONNECTION_IN_USE = 0x2E
};
class KnxIpConnectResponse : public KnxIpFrame
{
public:
KnxIpConnectResponse(IpParameterObject& parameters, uint16_t address, uint16_t port, uint8_t channel, uint8_t type);
KnxIpConnectResponse(uint8_t channel, uint8_t errorCode);
IpHostProtocolAddressInformation& controlEndpoint();
KnxIpCRD& crd();
private:
IpHostProtocolAddressInformation _controlEndpoint;
KnxIpCRD _crd;
};
#endif

41
src/knx/knx_ip_crd.cpp Normal file
View File

@ -0,0 +1,41 @@
#include "knx_ip_crd.h"
#ifdef USE_IP
KnxIpCRD::KnxIpCRD(uint8_t* data) : _data(data)
{}
KnxIpCRD::~KnxIpCRD()
{}
uint8_t KnxIpCRD::length() const
{
return *_data;
}
void KnxIpCRD::length(uint8_t value)
{
*_data = value;
}
uint8_t KnxIpCRD::type() const
{
return _data[1];
}
void KnxIpCRD::type(uint8_t value)
{
_data[1] = value;
}
uint16_t KnxIpCRD::address() const
{
uint16_t addr = _data[3];
addr |= _data[2] << 8;
return addr;
}
void KnxIpCRD::address(uint16_t value)
{
_data[2] = value >> 8;
_data[3] = value & 0xFF;
}
#endif

23
src/knx/knx_ip_crd.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include <cstdint>
#include "config.h"
#ifdef USE_IP
class KnxIpCRD
{
public:
KnxIpCRD(uint8_t* data);
virtual ~KnxIpCRD();
void address(uint16_t addr);
uint16_t address() const;
void type(uint8_t addr);
uint8_t type() const;
uint8_t length() const;
void length(uint8_t value);
protected:
uint8_t* _data = 0;
};
#endif

38
src/knx/knx_ip_cri.cpp Normal file
View File

@ -0,0 +1,38 @@
#include "knx_ip_cri.h"
#ifdef USE_IP
KnxIpCRI::KnxIpCRI(uint8_t* data) : _data(data)
{}
KnxIpCRI::~KnxIpCRI()
{}
uint8_t KnxIpCRI::length() const
{
return *_data;
}
void KnxIpCRI::length(uint8_t value)
{
*_data = value;
}
ConnectionType KnxIpCRI::type() const
{
return (ConnectionType)_data[1];
}
void KnxIpCRI::type(ConnectionType value)
{
_data[1] = value;
}
uint8_t KnxIpCRI::layer() const
{
return _data[2];
}
void KnxIpCRI::layer(uint8_t value)
{
_data[2] = value;
}
#endif

36
src/knx/knx_ip_cri.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <cstdint>
#include "config.h"
#ifdef USE_IP
#define LEN_CRI 4
//TODO vervollständigen
enum ConnectionType : uint8_t
{
DEVICE_MGMT_CONNECTION = 3,
TUNNEL_CONNECTION = 4,
REMLOG_CONNECTION = 6,
REMCONF_CONNECTION = 7,
OBJSVR_CONNECTION = 8
};
// Connection Request Information
class KnxIpCRI
{
public:
KnxIpCRI(uint8_t* data);
virtual ~KnxIpCRI();
ConnectionType type() const;
void type(ConnectionType value);
void layer(uint8_t layer);
uint8_t layer() const;
uint8_t length() const;
void length(uint8_t value);
protected:
uint8_t* _data = 0;
};
#endif

View File

@ -0,0 +1,13 @@
#include "knx_ip_description_request.h"
#ifdef USE_IP
KnxIpDescriptionRequest::KnxIpDescriptionRequest(uint8_t* data, uint16_t length)
: KnxIpFrame(data, length), _hpaiCtrl(data + LEN_KNXIP_HEADER)
{
}
IpHostProtocolAddressInformation& KnxIpDescriptionRequest::hpaiCtrl()
{
return _hpaiCtrl;
}
#endif

View File

@ -0,0 +1,15 @@
#pragma once
#include "knx_ip_frame.h"
#include "knx_ip_cri.h"
#include "ip_host_protocol_address_information.h"
#ifdef USE_IP
class KnxIpDescriptionRequest : public KnxIpFrame
{
public:
KnxIpDescriptionRequest(uint8_t* data, uint16_t length);
IpHostProtocolAddressInformation& hpaiCtrl();
private:
IpHostProtocolAddressInformation _hpaiCtrl;
};
#endif

View File

@ -0,0 +1,72 @@
#include "knx_ip_description_response.h"
#ifdef USE_IP
#define LEN_SERVICE_FAMILIES 2
#if MASK_VERSION == 0x091A
#ifdef KNX_TUNNELING
#define LEN_SERVICE_DIB (2 + 4 * LEN_SERVICE_FAMILIES)
#else
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
#endif
#else
#ifdef KNX_TUNNELING
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
#else
#define LEN_SERVICE_DIB (2 + 2 * LEN_SERVICE_FAMILIES)
#endif
#endif
KnxIpDescriptionResponse::KnxIpDescriptionResponse(IpParameterObject& parameters, DeviceObject& deviceObject)
: KnxIpFrame(LEN_KNXIP_HEADER + LEN_DEVICE_INFORMATION_DIB + LEN_SERVICE_DIB),
_deviceInfo(_data + LEN_KNXIP_HEADER),
_supportedServices(_data + LEN_KNXIP_HEADER + LEN_DEVICE_INFORMATION_DIB)
{
serviceTypeIdentifier(DescriptionResponse);
_deviceInfo.length(LEN_DEVICE_INFORMATION_DIB);
_deviceInfo.code(DEVICE_INFO);
#if MASK_VERSION == 0x57B0
_deviceInfo.medium(0x20); //MediumType is IP (for IP-Only Devices)
#else
_deviceInfo.medium(0x02); //MediumType is TP
#endif
_deviceInfo.status(deviceObject.progMode());
_deviceInfo.individualAddress(parameters.propertyValue<uint16_t>(PID_KNX_INDIVIDUAL_ADDRESS));
_deviceInfo.projectInstallationIdentifier(parameters.propertyValue<uint16_t>(PID_PROJECT_INSTALLATION_ID));
_deviceInfo.serialNumber(deviceObject.propertyData(PID_SERIAL_NUMBER));
_deviceInfo.routingMulticastAddress(parameters.propertyValue<uint32_t>(PID_ROUTING_MULTICAST_ADDRESS));
//_deviceInfo.routingMulticastAddress(0);
uint8_t mac_address[LEN_MAC_ADDRESS] = {0};
Property* prop = parameters.property(PID_MAC_ADDRESS);
prop->read(mac_address);
_deviceInfo.macAddress(mac_address);
uint8_t friendlyName[LEN_FRIENDLY_NAME] = {0};
prop = parameters.property(PID_FRIENDLY_NAME);
prop->read(1, LEN_FRIENDLY_NAME, friendlyName);
_deviceInfo.friendlyName(friendlyName);
_supportedServices.length(LEN_SERVICE_DIB);
_supportedServices.code(SUPP_SVC_FAMILIES);
_supportedServices.serviceVersion(Core, 1);
_supportedServices.serviceVersion(DeviceManagement, 1);
#ifdef KNX_TUNNELING
_supportedServices.serviceVersion(Tunnelling, 1);
#endif
#if MASK_VERSION == 0x091A
_supportedServices.serviceVersion(Routing, 1);
#endif
}
KnxIpDeviceInformationDIB& KnxIpDescriptionResponse::deviceInfo()
{
return _deviceInfo;
}
KnxIpSupportedServiceDIB& KnxIpDescriptionResponse::supportedServices()
{
return _supportedServices;
}
#endif

View File

@ -0,0 +1,21 @@
#pragma once
#include "knx_ip_frame.h"
#include "ip_host_protocol_address_information.h"
#include "knx_ip_device_information_dib.h"
#include "knx_ip_supported_service_dib.h"
#include "ip_parameter_object.h"
#ifdef USE_IP
class KnxIpDescriptionResponse : public KnxIpFrame
{
public:
KnxIpDescriptionResponse(IpParameterObject& parameters, DeviceObject& deviceObj);
KnxIpDeviceInformationDIB& deviceInfo();
KnxIpSupportedServiceDIB& supportedServices();
private:
KnxIpDeviceInformationDIB _deviceInfo;
KnxIpSupportedServiceDIB _supportedServices;
};
#endif

View File

@ -12,6 +12,9 @@ enum DescriptionTypeCode : uint8_t
IP_CONFIG = 0x03,
IP_CUR_CONFIG = 0x04,
KNX_ADDRESSES = 0x05,
MANUFACTURER_DATA = 0x06,
TUNNELING_INFO = 0x07,
EXTENDED_DEVICE_INFO = 0x08,
MFR_DATA = 0xFE
};

View File

@ -0,0 +1,26 @@
#include "knx_ip_disconnect_request.h"
#ifdef USE_IP
KnxIpDisconnectRequest::KnxIpDisconnectRequest(uint8_t* data, uint16_t length)
: KnxIpFrame(data, length), _hpaiCtrl(data + LEN_KNXIP_HEADER + 1 /*ChannelId*/ + 1 /*Reserved*/)
{
}
KnxIpDisconnectRequest::KnxIpDisconnectRequest()
: KnxIpFrame(1 /*ChannelId*/ + 1 /*Reserved*/ + LEN_KNXIP_HEADER + LEN_IPHPAI), _hpaiCtrl(_data + 1 /*ChannelId*/ + 1 /*Reserved*/ + LEN_KNXIP_HEADER)
{
serviceTypeIdentifier(DisconnectRequest);
}
IpHostProtocolAddressInformation& KnxIpDisconnectRequest::hpaiCtrl()
{
return _hpaiCtrl;
}
uint8_t KnxIpDisconnectRequest::channelId()
{
return _data[LEN_KNXIP_HEADER];
}
void KnxIpDisconnectRequest::channelId(uint8_t channelId)
{
_data[LEN_KNXIP_HEADER] = channelId;
}
#endif

View File

@ -0,0 +1,17 @@
#pragma once
#include "knx_ip_frame.h"
#include "ip_host_protocol_address_information.h"
#ifdef USE_IP
class KnxIpDisconnectRequest : public KnxIpFrame
{
public:
KnxIpDisconnectRequest(uint8_t* data, uint16_t length);
KnxIpDisconnectRequest();
IpHostProtocolAddressInformation& hpaiCtrl();
uint8_t channelId();
void channelId(uint8_t channelId);
private:
IpHostProtocolAddressInformation _hpaiCtrl;
};
#endif

View File

@ -0,0 +1,12 @@
#include "knx_ip_disconnect_response.h"
#ifdef USE_IP
KnxIpDisconnectResponse::KnxIpDisconnectResponse(uint8_t channel, uint8_t status)
: KnxIpFrame(LEN_KNXIP_HEADER + 1 /*Channel*/ + 1 /*Status*/)
{
serviceTypeIdentifier(DisconnectResponse);
_data[LEN_KNXIP_HEADER] = channel;
_data[LEN_KNXIP_HEADER+1] = status;
}
#endif

View File

@ -0,0 +1,13 @@
#pragma once
#include "knx_ip_frame.h"
#ifdef USE_IP
class KnxIpDisconnectResponse : public KnxIpFrame
{
public:
KnxIpDisconnectResponse(uint8_t channel, uint8_t status);
private:
};
#endif

View File

@ -0,0 +1,42 @@
#include "knx_ip_extended_device_information_dib.h"
#include "bits.h"
#ifdef USE_IP
KnxIpExtendedDeviceInformationDIB::KnxIpExtendedDeviceInformationDIB(uint8_t* data) : KnxIpDIB(data)
{}
uint8_t KnxIpExtendedDeviceInformationDIB::status() const
{
return _data[2];
}
void KnxIpExtendedDeviceInformationDIB::status(uint8_t value)
{
_data[2] = value;
}
uint16_t KnxIpExtendedDeviceInformationDIB::localMaxApdu() const
{
return getWord(_data + 4);
}
void KnxIpExtendedDeviceInformationDIB::localMaxApdu(uint16_t value)
{
pushWord(value, _data + 4);
}
uint16_t KnxIpExtendedDeviceInformationDIB::deviceDescriptor() const
{
return getWord(_data + 6);
}
void KnxIpExtendedDeviceInformationDIB::deviceDescriptor(uint16_t value)
{
pushWord(value, _data + 6);
}
#endif

View File

@ -0,0 +1,19 @@
#pragma once
#include "knx_ip_dib.h"
#ifdef USE_IP
#define LEN_EXTENDED_DEVICE_INFORMATION_DIB 8
class KnxIpExtendedDeviceInformationDIB : public KnxIpDIB
{
public:
KnxIpExtendedDeviceInformationDIB(uint8_t* data);
uint8_t status() const;
void status(uint8_t value);
uint16_t localMaxApdu() const;
void localMaxApdu(uint16_t value);
uint16_t deviceDescriptor() const;
void deviceDescriptor(uint16_t value);
};
#endif

View File

@ -0,0 +1,27 @@
#include "knx_ip_knx_addresses_dib.h"
#ifdef USE_IP
KnxIpKnxAddressesDIB::KnxIpKnxAddressesDIB(uint8_t* data) : KnxIpDIB(data)
{
currentPos = data + 4;
}
uint16_t KnxIpKnxAddressesDIB::individualAddress()
{
uint16_t addr = 0;
popWord(addr, _data + 2);
return addr;
}
void KnxIpKnxAddressesDIB::individualAddress(uint16_t addr)
{
pushInt(addr, _data + 2);
}
void KnxIpKnxAddressesDIB::additional(uint16_t addr)
{
pushWord(addr, currentPos);
currentPos += 2;
length(currentPos - _data);
}
#endif

View File

@ -0,0 +1,17 @@
#pragma once
#include "knx_ip_dib.h"
#include "bits.h"
#ifdef USE_IP
class KnxIpKnxAddressesDIB : public KnxIpDIB
{
public:
KnxIpKnxAddressesDIB(uint8_t* data);
uint16_t individualAddress();
void individualAddress(uint16_t addr);
void additional(uint16_t addr);
private:
uint8_t *currentPos = 0;
};
#endif

View File

@ -0,0 +1,59 @@
#include "knx_ip_search_request_extended.h"
#include "bits.h"
#include "service_families.h"
#if KNX_SERVICE_FAMILY_CORE >= 2
#ifdef USE_IP
KnxIpSearchRequestExtended::KnxIpSearchRequestExtended(uint8_t* data, uint16_t length)
: KnxIpFrame(data, length), _hpai(data + LEN_KNXIP_HEADER)
{
if(length == LEN_KNXIP_HEADER + LEN_IPHPAI) return; //we dont have SRPs
int currentPos = LEN_KNXIP_HEADER + LEN_IPHPAI;
while(currentPos < length)
{
switch(data[currentPos+1])
{
case 0x01:
srpByProgMode = true;
break;
case 0x02:
srpByMacAddr = true;
srpMacAddr = data + currentPos + 2;
break;
case 0x03:
srpByService = true;
srpServiceFamilies = data + currentPos;
break;
case 0x04:
srpRequestDIBs = true;
for(int i = 0; i < data[currentPos]-2; i++)
{
if(data[currentPos+i+2] == 0) continue;
if(data[currentPos+i+2] > REQUESTED_DIBS_MAX)
{
print("Requested DIBs too high ");
continue;
}
requestedDIBs[data[currentPos+i+2]] = true;
}
break;
}
currentPos += data[currentPos];
};
}
IpHostProtocolAddressInformation& KnxIpSearchRequestExtended::hpai()
{
return _hpai;
}
bool KnxIpSearchRequestExtended::requestedDIB(uint8_t code)
{
if(code > REQUESTED_DIBS_MAX) return false;
return requestedDIBs[code];
}
#endif
#endif

View File

@ -0,0 +1,26 @@
#pragma once
#include "service_families.h"
#if KNX_SERVICE_FAMILY_CORE >= 2
#include "knx_ip_frame.h"
#include "ip_host_protocol_address_information.h"
#ifdef USE_IP
#define REQUESTED_DIBS_MAX 9
class KnxIpSearchRequestExtended : public KnxIpFrame
{
public:
KnxIpSearchRequestExtended(uint8_t* data, uint16_t length);
IpHostProtocolAddressInformation& hpai();
bool requestedDIB(uint8_t code);
bool srpByProgMode = false;
bool srpByMacAddr = false;
bool srpByService = false;
bool srpRequestDIBs = false;
uint8_t *srpMacAddr = nullptr;
uint8_t *srpServiceFamilies = nullptr;
private:
IpHostProtocolAddressInformation _hpai;
bool requestedDIBs[REQUESTED_DIBS_MAX]; //for now only 1 to 8
};
#endif
#endif

View File

@ -1,10 +1,23 @@
#include "knx_ip_search_response.h"
#ifdef USE_IP
#define SERVICE_FAMILIES 2
#define LEN_SERVICE_FAMILIES 2
#if MASK_VERSION == 0x091A
#ifdef KNX_TUNNELING
#define LEN_SERVICE_DIB (2 + 4 * LEN_SERVICE_FAMILIES)
#else
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
#endif
#else
#ifdef KNX_TUNNELING
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
#else
#define LEN_SERVICE_DIB (2 + 2 * LEN_SERVICE_FAMILIES)
#endif
#endif
KnxIpSearchResponse::KnxIpSearchResponse(IpParameterObject& parameters, DeviceObject& deviceObject)
: KnxIpFrame(LEN_KNXIP_HEADER + LEN_IPHPAI + LEN_DEVICE_INFORMATION_DIB + 2 + 2 * SERVICE_FAMILIES),
: KnxIpFrame(LEN_KNXIP_HEADER + LEN_IPHPAI + LEN_DEVICE_INFORMATION_DIB + LEN_SERVICE_DIB),
_controlEndpoint(_data + LEN_KNXIP_HEADER), _deviceInfo(_data + LEN_KNXIP_HEADER + LEN_IPHPAI),
_supportedServices(_data + LEN_KNXIP_HEADER + LEN_IPHPAI + LEN_DEVICE_INFORMATION_DIB)
{
@ -17,7 +30,11 @@ KnxIpSearchResponse::KnxIpSearchResponse(IpParameterObject& parameters, DeviceOb
_deviceInfo.length(LEN_DEVICE_INFORMATION_DIB);
_deviceInfo.code(DEVICE_INFO);
_deviceInfo.medium(0x20); //KNX-IP FIXME get this value from somewhere else
#if MASK_VERSION == 0x57B0
_deviceInfo.medium(0x20); //MediumType is IP (for IP-Only Devices)
#else
_deviceInfo.medium(0x02); //MediumType is TP
#endif
_deviceInfo.status(deviceObject.progMode());
_deviceInfo.individualAddress(parameters.propertyValue<uint16_t>(PID_KNX_INDIVIDUAL_ADDRESS));
_deviceInfo.projectInstallationIdentifier(parameters.propertyValue<uint16_t>(PID_PROJECT_INSTALLATION_ID));
@ -35,11 +52,16 @@ KnxIpSearchResponse::KnxIpSearchResponse(IpParameterObject& parameters, DeviceOb
prop->read(1, LEN_FRIENDLY_NAME, friendlyName);
_deviceInfo.friendlyName(friendlyName);
_supportedServices.length(2 + 2 * SERVICE_FAMILIES);
_supportedServices.length(LEN_SERVICE_DIB);
_supportedServices.code(SUPP_SVC_FAMILIES);
_supportedServices.serviceVersion(Core, 1);
_supportedServices.serviceVersion(DeviceManagement, 1);
// _supportedServices.serviceVersion(Routing, 1);
_supportedServices.serviceVersion(Core, KNX_SERVICE_FAMILY_CORE);
_supportedServices.serviceVersion(DeviceManagement, KNX_SERVICE_FAMILY_DEVICE_MANAGEMENT);
#ifdef KNX_TUNNELING
_supportedServices.serviceVersion(Tunnelling, KNX_SERVICE_FAMILY_TUNNELING);
#endif
#if MASK_VERSION == 0x091A
_supportedServices.serviceVersion(Routing, KNX_SERVICE_FAMILY_ROUTING);
#endif
}

View File

@ -5,6 +5,7 @@
#include "knx_ip_device_information_dib.h"
#include "knx_ip_supported_service_dib.h"
#include "ip_parameter_object.h"
#include "service_families.h"
#ifdef USE_IP
class KnxIpSearchResponse : public KnxIpFrame

View File

@ -0,0 +1,221 @@
#include "knx_ip_search_response_extended.h"
#include "service_families.h"
#if KNX_SERVICE_FAMILY_CORE >= 2
#ifdef USE_IP
#define LEN_SERVICE_FAMILIES 2
#if MASK_VERSION == 0x091A
#ifdef KNX_TUNNELING
#define LEN_SERVICE_DIB (2 + 4 * LEN_SERVICE_FAMILIES)
#else
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
#endif
#else
#ifdef KNX_TUNNELING
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
#else
#define LEN_SERVICE_DIB (2 + 2 * LEN_SERVICE_FAMILIES)
#endif
#endif
KnxIpSearchResponseExtended::KnxIpSearchResponseExtended(IpParameterObject& parameters, DeviceObject& deviceObject, int dibLength)
: KnxIpFrame(LEN_KNXIP_HEADER + LEN_IPHPAI + dibLength),
_controlEndpoint(_data + LEN_KNXIP_HEADER)
{
serviceTypeIdentifier(SearchResponseExt);
_controlEndpoint.length(LEN_IPHPAI);
_controlEndpoint.code(IPV4_UDP);
_controlEndpoint.ipAddress(parameters.propertyValue<uint32_t>(PID_CURRENT_IP_ADDRESS));
_controlEndpoint.ipPortNumber(KNXIP_MULTICAST_PORT);
currentPos = LEN_KNXIP_HEADER + LEN_IPHPAI;
}
void KnxIpSearchResponseExtended::setDeviceInfo(IpParameterObject& parameters, DeviceObject& deviceObject)
{
println("setDeviceInfo");
KnxIpDeviceInformationDIB _deviceInfo(_data + currentPos);
_deviceInfo.length(LEN_DEVICE_INFORMATION_DIB);
_deviceInfo.code(DEVICE_INFO);
#if MASK_VERSION == 0x57B0
_deviceInfo.medium(0x20); //MediumType is IP (for IP-Only Devices)
#else
_deviceInfo.medium(0x02); //MediumType is TP
#endif
_deviceInfo.status(deviceObject.progMode());
_deviceInfo.individualAddress(parameters.propertyValue<uint16_t>(PID_KNX_INDIVIDUAL_ADDRESS));
_deviceInfo.projectInstallationIdentifier(parameters.propertyValue<uint16_t>(PID_PROJECT_INSTALLATION_ID));
_deviceInfo.serialNumber(deviceObject.propertyData(PID_SERIAL_NUMBER));
_deviceInfo.routingMulticastAddress(parameters.propertyValue<uint32_t>(PID_ROUTING_MULTICAST_ADDRESS));
//_deviceInfo.routingMulticastAddress(0);
uint8_t mac_address[LEN_MAC_ADDRESS] = {0};
Property* prop = parameters.property(PID_MAC_ADDRESS);
prop->read(mac_address);
_deviceInfo.macAddress(mac_address);
uint8_t friendlyName[LEN_FRIENDLY_NAME] = {0};
prop = parameters.property(PID_FRIENDLY_NAME);
prop->read(1, LEN_FRIENDLY_NAME, friendlyName);
_deviceInfo.friendlyName(friendlyName);
currentPos += LEN_DEVICE_INFORMATION_DIB;
}
void KnxIpSearchResponseExtended::setSupportedServices()
{
println("setSupportedServices");
KnxIpSupportedServiceDIB _supportedServices(_data + currentPos);
_supportedServices.length(LEN_SERVICE_DIB);
_supportedServices.code(SUPP_SVC_FAMILIES);
_supportedServices.serviceVersion(Core, KNX_SERVICE_FAMILY_CORE);
_supportedServices.serviceVersion(DeviceManagement, KNX_SERVICE_FAMILY_DEVICE_MANAGEMENT);
#ifdef KNX_TUNNELING
_supportedServices.serviceVersion(Tunnelling, KNX_SERVICE_FAMILY_TUNNELING);
#endif
#if MASK_VERSION == 0x091A
_supportedServices.serviceVersion(Routing, KNX_SERVICE_FAMILY_ROUTING);
#endif
currentPos += LEN_SERVICE_DIB;
}
void KnxIpSearchResponseExtended::setIpConfig(IpParameterObject& parameters)
{
println("setIpConfig");
KnxIpConfigDIB _ipConfig(_data + currentPos);
_ipConfig.length(LEN_IP_CONFIG_DIB);
_ipConfig.code(IP_CONFIG);
_ipConfig.address(parameters.propertyValue<uint32_t>(PID_IP_ADDRESS));
_ipConfig.subnet(parameters.propertyValue<uint32_t>(PID_SUBNET_MASK));
_ipConfig.gateway(parameters.propertyValue<uint32_t>(PID_DEFAULT_GATEWAY));
_ipConfig.info1(parameters.propertyValue<uint8_t>(PID_IP_CAPABILITIES));
_ipConfig.info2(parameters.propertyValue<uint8_t>(PID_IP_ASSIGNMENT_METHOD));
currentPos += LEN_IP_CONFIG_DIB;
}
void KnxIpSearchResponseExtended::setIpCurrentConfig(IpParameterObject& parameters)
{
println("setIpCurrentConfig");
KnxIpConfigDIB _ipCurConfig(_data + currentPos, true);
_ipCurConfig.length(LEN_IP_CURRENT_CONFIG_DIB);
_ipCurConfig.code(IP_CUR_CONFIG);
_ipCurConfig.address(parameters.propertyValue<uint32_t>(PID_CURRENT_IP_ADDRESS));
_ipCurConfig.subnet(parameters.propertyValue<uint32_t>(PID_CURRENT_SUBNET_MASK));
_ipCurConfig.gateway(parameters.propertyValue<uint32_t>(PID_CURRENT_DEFAULT_GATEWAY));
_ipCurConfig.dhcp(parameters.propertyValue<uint32_t>(PID_DHCP_BOOTP_SERVER));
_ipCurConfig.info1(parameters.propertyValue<uint8_t>(PID_CURRENT_IP_ASSIGNMENT_METHOD));
_ipCurConfig.info2(0x00); //Reserved
currentPos += LEN_IP_CURRENT_CONFIG_DIB;
}
void KnxIpSearchResponseExtended::setKnxAddresses(IpParameterObject& parameters, DeviceObject& deviceObject)
{
println("setKnxAddresses");
KnxIpKnxAddressesDIB _knxAddresses(_data + currentPos);
_knxAddresses.length(4); //minimum
_knxAddresses.code(KNX_ADDRESSES);
_knxAddresses.individualAddress(deviceObject.individualAddress());
uint16_t length = 0;
parameters.readPropertyLength(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES, length);
const uint8_t *addresses = parameters.propertyData(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES);
for(int i = 0; i < length; i++)
{
uint16_t additional = 0;
popWord(additional, addresses + i*2);
_knxAddresses.additional(additional);
}
currentPos += _knxAddresses.length();
}
void KnxIpSearchResponseExtended::setTunnelingInfo(IpParameterObject& parameters, DeviceObject& deviceObject, KnxIpTunnelConnection tunnels[])
{
println("setTunnelingInfo");
KnxIpTunnelingInfoDIB _tunnelInfo(_data + currentPos);
_tunnelInfo.length(4); //minlength
_tunnelInfo.code(TUNNELING_INFO);
_tunnelInfo.apduLength(254); //FIXME where to get from
uint16_t length = 0;
parameters.readPropertyLength(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES, length);
const uint8_t *addresses;
if(length == KNX_TUNNELING)
{
addresses = parameters.propertyData(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES);
} else {
uint8_t addrbuffer[KNX_TUNNELING*2];
addresses = (uint8_t*)addrbuffer;
for(int i = 0; i < KNX_TUNNELING; i++)
{
addrbuffer[i*2+1] = i+1;
addrbuffer[i*2] = deviceObject.individualAddress() / 0x0100;
}
}
for(int i = 0; i < length; i++)
{
uint16_t additional = 0;
popWord(additional, addresses + i*2);
uint16_t flags = 0;
uint8_t doubleCounter = 0;
bool used = false;
for(int i = 0; i < KNX_TUNNELING; i++)
{
if(tunnels[i].IndividualAddress == additional)
{
doubleCounter += 1;
if(tunnels[i].ChannelId != 0)
used = true;
}
}
if(doubleCounter > 1 && used)
flags |= 1 << 2; //Slot is not usable; double PA is already used
if(used)
{
flags |= 1 << 2; //Slot is not usable; PA is already used
flags |= 1; //Slot is not free
}
flags = ~flags;
_tunnelInfo.tunnelingSlot(additional, flags);
}
currentPos += _tunnelInfo.length();
}
void KnxIpSearchResponseExtended::setExtendedDeviceInfo()
{
println("setExtendedDeviceInfo");
KnxIpExtendedDeviceInformationDIB _extended(_data + currentPos);
_extended.length(LEN_EXTENDED_DEVICE_INFORMATION_DIB);
_extended.code(EXTENDED_DEVICE_INFO);
_extended.status(0x01); //FIXME dont know encoding PID_MEDIUM_STATUS=51 RouterObject
_extended.localMaxApdu(254); //FIXME is this correct?
_extended.deviceDescriptor(MASK_VERSION);
currentPos += LEN_EXTENDED_DEVICE_INFORMATION_DIB;
}
IpHostProtocolAddressInformation& KnxIpSearchResponseExtended::controlEndpoint()
{
return _controlEndpoint;
}
uint8_t *KnxIpSearchResponseExtended::DIBs()
{
return _data + LEN_KNXIP_HEADER + LEN_IPHPAI;
}
#endif
#endif

View File

@ -0,0 +1,38 @@
#pragma once
#include "service_families.h"
#if KNX_SERVICE_FAMILY_CORE >= 2
#include "knx_ip_frame.h"
#include "ip_host_protocol_address_information.h"
#include "knx_ip_device_information_dib.h"
#include "knx_ip_extended_device_information_dib.h"
#include "knx_ip_supported_service_dib.h"
#include "knx_ip_config_dib.h"
#include "knx_ip_knx_addresses_dib.h"
#include "knx_ip_tunneling_info_dib.h"
#include "ip_parameter_object.h"
#include "knx_ip_tunnel_connection.h"
#ifdef USE_IP
class KnxIpSearchResponseExtended : public KnxIpFrame
{
public:
KnxIpSearchResponseExtended(IpParameterObject& parameters, DeviceObject& deviceObj, int dibLength);
IpHostProtocolAddressInformation& controlEndpoint();
void setDeviceInfo(IpParameterObject& parameters, DeviceObject& deviceObject);
void setSupportedServices();
void setIpConfig(IpParameterObject& parameters);
void setIpCurrentConfig(IpParameterObject& parameters);
void setKnxAddresses(IpParameterObject& parameters, DeviceObject& deviceObject);
//setManuData
void setTunnelingInfo(IpParameterObject& parameters, DeviceObject& deviceObject, KnxIpTunnelConnection tunnels[]);
void setExtendedDeviceInfo();
uint8_t *DIBs();
private:
IpHostProtocolAddressInformation _controlEndpoint;
int currentPos = 0;
};
#endif
#endif

View File

@ -0,0 +1,16 @@
#include "knx_ip_state_request.h"
#ifdef USE_IP
KnxIpStateRequest::KnxIpStateRequest(uint8_t* data, uint16_t length)
: KnxIpFrame(data, length), _hpaiCtrl(data + LEN_KNXIP_HEADER + 1 /*ChannelId*/ + 1 /*Reserved*/)
{
}
IpHostProtocolAddressInformation& KnxIpStateRequest::hpaiCtrl()
{
return _hpaiCtrl;
}
uint8_t KnxIpStateRequest::channelId()
{
return _data[LEN_KNXIP_HEADER];
}
#endif

View File

@ -0,0 +1,17 @@
#pragma once
#include "knx_ip_frame.h"
#include "knx_ip_cri.h"
#include "ip_host_protocol_address_information.h"
#ifdef USE_IP
class KnxIpStateRequest : public KnxIpFrame
{
public:
KnxIpStateRequest(uint8_t* data, uint16_t length);
IpHostProtocolAddressInformation& hpaiCtrl();
uint8_t channelId();
private:
IpHostProtocolAddressInformation _hpaiCtrl;
};
#endif

View File

@ -0,0 +1,26 @@
#include "knx_ip_state_response.h"
#ifdef USE_IP
#define LEN_SERVICE_FAMILIES 2
#if MASK_VERSION == 0x091A
#ifdef KNX_TUNNELING
#define LEN_SERVICE_DIB (2 + 4 * LEN_SERVICE_FAMILIES)
#else
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
#endif
#else
#ifdef KNX_TUNNELING
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
#else
#define LEN_SERVICE_DIB (2 + 2 * LEN_SERVICE_FAMILIES)
#endif
#endif
KnxIpStateResponse::KnxIpStateResponse(uint8_t channelId, uint8_t errorCode)
: KnxIpFrame(LEN_KNXIP_HEADER + 2)
{
serviceTypeIdentifier(ConnectionStateResponse);
_data[LEN_KNXIP_HEADER] = channelId;
_data[LEN_KNXIP_HEADER + 1] = errorCode;
}
#endif

View File

@ -0,0 +1,13 @@
#pragma once
#include "knx_ip_frame.h"
#ifdef USE_IP
class KnxIpStateResponse : public KnxIpFrame
{
public:
KnxIpStateResponse(uint8_t channelId, uint8_t errorCode);
private:
};
#endif

View File

@ -0,0 +1,19 @@
#include "knx_ip_tunnel_connection.h"
KnxIpTunnelConnection::KnxIpTunnelConnection()
{
}
void KnxIpTunnelConnection::Reset()
{
ChannelId = 0;
IpAddress = 0;
PortData = 0;
PortCtrl = 0;
lastHeartbeat = 0;
SequenceCounter_S = 0;
SequenceCounter_R = 255;
IndividualAddress = 0;
IsConfig = false;
}

View File

@ -0,0 +1,24 @@
#pragma once
#include "config.h"
#include "platform.h"
#include "bits.h"
class KnxIpTunnelConnection
{
public:
KnxIpTunnelConnection();
uint8_t ChannelId = 0;
uint16_t IndividualAddress = 0;
uint32_t IpAddress = 0;
uint16_t PortData = 0;
uint16_t PortCtrl = 0;
uint8_t SequenceCounter_S = 0;
uint8_t SequenceCounter_R = 255;
unsigned long lastHeartbeat = 0;
bool IsConfig = false;
void Reset();
private:
};

View File

@ -0,0 +1,20 @@
#include "knx_ip_tunneling_ack.h"
#include <cstring>
#ifdef USE_IP
KnxIpTunnelingAck::KnxIpTunnelingAck(uint8_t* data,
uint16_t length) : KnxIpFrame(data, length), _ch(_data + LEN_KNXIP_HEADER)
{
}
KnxIpTunnelingAck::KnxIpTunnelingAck()
: KnxIpFrame(LEN_KNXIP_HEADER + LEN_CH), _ch(_data + LEN_KNXIP_HEADER)
{
serviceTypeIdentifier(TunnelingAck);
}
KnxIpCH& KnxIpTunnelingAck::connectionHeader()
{
return _ch;
}
#endif

View File

@ -0,0 +1,17 @@
#pragma once
#include "knx_ip_frame.h"
#include "cemi_frame.h"
#include "knx_ip_ch.h"
#ifdef USE_IP
class KnxIpTunnelingAck : public KnxIpFrame
{
public:
KnxIpTunnelingAck(uint8_t* data, uint16_t length);
KnxIpTunnelingAck();
KnxIpCH& connectionHeader();
private:
KnxIpCH _ch;
};
#endif

View File

@ -0,0 +1,31 @@
#include "knx_ip_tunneling_info_dib.h"
#include "service_families.h"
#if KNX_SERVICE_FAMILY_CORE >= 2
#ifdef USE_IP
KnxIpTunnelingInfoDIB::KnxIpTunnelingInfoDIB(uint8_t* data) : KnxIpDIB(data)
{
currentPos = data + 4;
}
uint16_t KnxIpTunnelingInfoDIB::apduLength()
{
uint16_t addr = 0;
popWord(addr, _data+2);
return addr;
}
void KnxIpTunnelingInfoDIB::apduLength(uint16_t addr)
{
pushWord(addr, _data+2);
}
void KnxIpTunnelingInfoDIB::tunnelingSlot(uint16_t addr, uint16_t state)
{
pushWord(addr, currentPos);
pushWord(state, currentPos + 2);
currentPos += 4;
length(currentPos - _data);
}
#endif
#endif

View File

@ -0,0 +1,20 @@
#pragma once
#include "knx_ip_dib.h"
#include "bits.h"
#include "service_families.h"
#if KNX_SERVICE_FAMILY_CORE >= 2
#ifdef USE_IP
class KnxIpTunnelingInfoDIB : public KnxIpDIB
{
public:
KnxIpTunnelingInfoDIB(uint8_t* data);
uint16_t apduLength();
void apduLength(uint16_t addr);
void tunnelingSlot(uint16_t addr, uint16_t state);
private:
uint8_t *currentPos = 0;
};
#endif
#endif

View File

@ -0,0 +1,26 @@
#include "knx_ip_tunneling_request.h"
#include <cstring>
#ifdef USE_IP
KnxIpTunnelingRequest::KnxIpTunnelingRequest(uint8_t* data,
uint16_t length) : KnxIpFrame(data, length), _ch(_data + headerLength()), _frame(data + LEN_CH + headerLength(), length - LEN_CH - headerLength())
{
}
KnxIpTunnelingRequest::KnxIpTunnelingRequest(CemiFrame frame)
: KnxIpFrame(frame.totalLenght() + LEN_CH + LEN_KNXIP_HEADER), _ch(_data + LEN_KNXIP_HEADER), _frame(_data + LEN_CH + LEN_KNXIP_HEADER, frame.totalLenght())
{
serviceTypeIdentifier(TunnelingRequest);
memcpy(_data + LEN_KNXIP_HEADER + LEN_CH, frame.data(), frame.totalLenght());
}
CemiFrame& KnxIpTunnelingRequest::frame()
{
return _frame;
}
KnxIpCH& KnxIpTunnelingRequest::connectionHeader()
{
return _ch;
}
#endif

View File

@ -0,0 +1,19 @@
#pragma once
#include "knx_ip_frame.h"
#include "cemi_frame.h"
#include "knx_ip_ch.h"
#ifdef USE_IP
class KnxIpTunnelingRequest : public KnxIpFrame
{
public:
KnxIpTunnelingRequest(uint8_t* data, uint16_t length);
KnxIpTunnelingRequest(CemiFrame frame);
CemiFrame& frame();
KnxIpCH& connectionHeader();
private:
CemiFrame _frame;
KnxIpCH _ch;
};
#endif

View File

@ -20,6 +20,15 @@ enum AckType
AckRequested = 0x2, //!< We want a DataLinkLayer acknowledgement.
};
enum TPAckType
{
// see U_ACK_REQ defines in tpuart_data_link_layer.cpp
AckReqNack = 0x04,
AckReqBusy = 0x02,
AckReqAck = 0x01,
AckReqNone = 0x0,
};
enum AddressType
{
IndividualAddress = 0,
@ -190,6 +199,12 @@ enum ApduType
DeviceDescriptorResponse = 0x340,
Restart = 0x380,
RestartMasterReset = 0x381,
RoutingTableOpen = 0x3C0,
RoutingTableRead = 0x3C1,
RoutingTableReadResponse = 0x3C2,
RoutingTableWrite = 0x3C3,
MemoryRouterWrite = 0x3CA,
MemoryRouterReadResponse = 0x3C9,
AuthorizeRequest = 0x3d1,
AuthorizeResponse = 0x3d2,
KeyWrite = 0x3d3,
@ -244,3 +259,32 @@ enum DptMedium
KNX_RF = 0x02,
KNX_IP = 0x05
};
enum LCGRPCONFIG
{
GROUP_6FFF = 0b00000011,
GROUP_7000 = 0b00001100,
GROUP_REPEAT = 0b00010000,
GROUP_6FFFUNLOCK = 0b00000001,
GROUP_6FFFLOCK = 0b00000010,
GROUP_6FFFROUTE = 0b00000011,
GROUP_7000UNLOCK = 0b00000100,
GROUP_7000LOCK = 0b00001000,
GROUP_7000ROUTE = 0b00001100
};
enum LCCONFIG
{
PHYS_FRAME = 0b00000011,
PHYS_FRAME_UNLOCK = 0b00000001,
PHYS_FRAME_LOCK = 0b00000010,
PHYS_FRAME_ROUT = 0b00000011,
PHYS_REPEAT = 0b00000100,
BROADCAST_LOCK = 0b00001000,
BROADCAST_REPEAT = 0b00010000,
GROUP_IACK_ROUT = 0b00100000,
PHYS_IACK = 0b11000000,
PHYS_IACK_NORMAL = 0b01000000,
PHYS_IACK_ALL = 0b10000000,
PHYS_IACK_NACK = 0b11000000
};

View File

@ -114,7 +114,7 @@ void Memory::readMemory()
buffer = _tableObjects[i]->restore(buffer);
uint16_t memorySize = 0;
buffer = popWord(memorySize, buffer);
println(memorySize);
if (memorySize == 0)
continue;
@ -279,6 +279,11 @@ void Memory::writeMemory(uint32_t relativeAddress, size_t size, uint8_t* data)
_platform.writeNonVolatileMemory(relativeAddress, data, size);
}
void Memory::readMemory(uint32_t relativeAddress, size_t size, uint8_t* data)
{
_platform.readNonVolatileMemory(relativeAddress, data, size);
}
uint8_t* Memory::toAbsolute(uint32_t relativeAddress)
{

View File

@ -35,7 +35,9 @@ typedef VersionCheckResult (*VersionCheckCallback)(uint16_t manufacturerId, uint
class Memory
{
public:
friend class TableObject;
public:
Memory(Platform& platform, DeviceObject& deviceObject);
virtual ~Memory();
void readMemory();
@ -47,6 +49,7 @@ public:
uint8_t* allocMemory(size_t size);
void freeMemory(uint8_t* ptr);
void writeMemory(uint32_t relativeAddress, size_t size, uint8_t* data);
void readMemory(uint32_t relativeAddress, size_t size, uint8_t* data);
uint8_t* toAbsolute(uint32_t relativeAddress);
uint32_t toRelative(uint8_t* absoluteAddress);

Some files were not shown because too many files have changed in this diff Show More