mirror of
https://github.com/thelsing/knx.git
synced 2024-10-20 20:07:45 +02:00
commit
44f90bb949
10
.vscode/extensions.json
vendored
10
.vscode/extensions.json
vendored
@ -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"
|
||||
]
|
||||
}
|
@ -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)
|
||||
|
47
README.md
47
README.md
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
16
library.json
Normal 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"
|
||||
}
|
||||
}
|
@ -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
|
@ -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()
|
||||
{
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -6,7 +6,11 @@
|
||||
#include <cstring>
|
||||
|
||||
ApplicationProgramObject::ApplicationProgramObject(Memory& memory)
|
||||
#if MASK_VERSION == 0x091A
|
||||
: TableObject(memory, 0x0100, 0x0100)
|
||||
#else
|
||||
: TableObject(memory)
|
||||
#endif
|
||||
{
|
||||
Property* properties[] =
|
||||
{
|
||||
|
@ -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)
|
||||
{
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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,
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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);
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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;
|
||||
|
@ -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
|
@ -12,6 +12,7 @@ enum HostProtocolCode : uint8_t
|
||||
#ifdef USE_IP
|
||||
|
||||
#define LEN_IPHPAI 8
|
||||
#define LEN_CRD 4
|
||||
|
||||
class IpHostProtocolAddressInformation
|
||||
{
|
||||
|
@ -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
48
src/knx/knx_ip_ch.cpp
Normal 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
28
src/knx/knx_ip_ch.h
Normal 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
|
91
src/knx/knx_ip_config_dib.cpp
Normal file
91
src/knx/knx_ip_config_dib.cpp
Normal 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
|
28
src/knx/knx_ip_config_dib.h
Normal file
28
src/knx/knx_ip_config_dib.h
Normal 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
|
17
src/knx/knx_ip_config_request.cpp
Normal file
17
src/knx/knx_ip_config_request.cpp
Normal 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
|
17
src/knx/knx_ip_config_request.h
Normal file
17
src/knx/knx_ip_config_request.h
Normal 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
|
21
src/knx/knx_ip_connect_request.cpp
Normal file
21
src/knx/knx_ip_connect_request.cpp
Normal 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
|
19
src/knx/knx_ip_connect_request.h
Normal file
19
src/knx/knx_ip_connect_request.h
Normal 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
|
45
src/knx/knx_ip_connect_response.cpp
Normal file
45
src/knx/knx_ip_connect_response.cpp
Normal 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
|
45
src/knx/knx_ip_connect_response.h
Normal file
45
src/knx/knx_ip_connect_response.h
Normal 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
41
src/knx/knx_ip_crd.cpp
Normal 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
23
src/knx/knx_ip_crd.h
Normal 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
38
src/knx/knx_ip_cri.cpp
Normal 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
36
src/knx/knx_ip_cri.h
Normal 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
|
13
src/knx/knx_ip_description_request.cpp
Normal file
13
src/knx/knx_ip_description_request.cpp
Normal 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
|
15
src/knx/knx_ip_description_request.h
Normal file
15
src/knx/knx_ip_description_request.h
Normal 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
|
72
src/knx/knx_ip_description_response.cpp
Normal file
72
src/knx/knx_ip_description_response.cpp
Normal 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
|
21
src/knx/knx_ip_description_response.h
Normal file
21
src/knx/knx_ip_description_response.h
Normal 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
|
@ -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
|
||||
};
|
||||
|
||||
|
26
src/knx/knx_ip_disconnect_request.cpp
Normal file
26
src/knx/knx_ip_disconnect_request.cpp
Normal 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
|
17
src/knx/knx_ip_disconnect_request.h
Normal file
17
src/knx/knx_ip_disconnect_request.h
Normal 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
|
12
src/knx/knx_ip_disconnect_response.cpp
Normal file
12
src/knx/knx_ip_disconnect_response.cpp
Normal 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
|
13
src/knx/knx_ip_disconnect_response.h
Normal file
13
src/knx/knx_ip_disconnect_response.h
Normal 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
|
42
src/knx/knx_ip_extended_device_information_dib.cpp
Normal file
42
src/knx/knx_ip_extended_device_information_dib.cpp
Normal 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
|
19
src/knx/knx_ip_extended_device_information_dib.h
Normal file
19
src/knx/knx_ip_extended_device_information_dib.h
Normal 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
|
27
src/knx/knx_ip_knx_addresses_dib.cpp
Normal file
27
src/knx/knx_ip_knx_addresses_dib.cpp
Normal 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
|
17
src/knx/knx_ip_knx_addresses_dib.h
Normal file
17
src/knx/knx_ip_knx_addresses_dib.h
Normal 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
|
59
src/knx/knx_ip_search_request_extended.cpp
Normal file
59
src/knx/knx_ip_search_request_extended.cpp
Normal 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
|
26
src/knx/knx_ip_search_request_extended.h
Normal file
26
src/knx/knx_ip_search_request_extended.h
Normal 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
|
@ -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
|
||||
}
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
221
src/knx/knx_ip_search_response_extended.cpp
Normal file
221
src/knx/knx_ip_search_response_extended.cpp
Normal 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
|
38
src/knx/knx_ip_search_response_extended.h
Normal file
38
src/knx/knx_ip_search_response_extended.h
Normal 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
|
16
src/knx/knx_ip_state_request.cpp
Normal file
16
src/knx/knx_ip_state_request.cpp
Normal 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
|
17
src/knx/knx_ip_state_request.h
Normal file
17
src/knx/knx_ip_state_request.h
Normal 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
|
26
src/knx/knx_ip_state_response.cpp
Normal file
26
src/knx/knx_ip_state_response.cpp
Normal 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
|
13
src/knx/knx_ip_state_response.h
Normal file
13
src/knx/knx_ip_state_response.h
Normal 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
|
19
src/knx/knx_ip_tunnel_connection.cpp
Normal file
19
src/knx/knx_ip_tunnel_connection.cpp
Normal 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;
|
||||
}
|
24
src/knx/knx_ip_tunnel_connection.h
Normal file
24
src/knx/knx_ip_tunnel_connection.h
Normal 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:
|
||||
|
||||
};
|
20
src/knx/knx_ip_tunneling_ack.cpp
Normal file
20
src/knx/knx_ip_tunneling_ack.cpp
Normal 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
|
17
src/knx/knx_ip_tunneling_ack.h
Normal file
17
src/knx/knx_ip_tunneling_ack.h
Normal 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
|
31
src/knx/knx_ip_tunneling_info_dib.cpp
Normal file
31
src/knx/knx_ip_tunneling_info_dib.cpp
Normal 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
|
20
src/knx/knx_ip_tunneling_info_dib.h
Normal file
20
src/knx/knx_ip_tunneling_info_dib.h
Normal 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
|
26
src/knx/knx_ip_tunneling_request.cpp
Normal file
26
src/knx/knx_ip_tunneling_request.cpp
Normal 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
|
19
src/knx/knx_ip_tunneling_request.h
Normal file
19
src/knx/knx_ip_tunneling_request.h
Normal 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
|
@ -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
|
||||
};
|
@ -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)
|
||||
{
|
||||
|
@ -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
Loading…
Reference in New Issue
Block a user