First Try

This commit is contained in:
Thomas Peterson 2026-01-17 12:22:45 +01:00
parent efde86778b
commit 53e123dc8a
209 changed files with 35370 additions and 412 deletions

View File

@ -3,6 +3,14 @@
cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
# idf_build_set_property(MINIMAL_BUILD ON)
add_definitions(
-Wno-unknown-pragmas
-DMASK_VERSION=0x07B0
-DKNX_NO_AUTOMATIC_GLOBAL_INSTANCE
-DKNX_FLASH_SIZE=4096
#-DKNX_NO_PRINT
#-Wno-stringop-truncation
)
project(knxdisplay)

View File

@ -0,0 +1,11 @@
# Define the directory containing your source files
set(SOURCE_DIR "./src")
set(SOURCE_DIR_1 "./src/knx")
# Use file(GLOB) to find all .cpp files in the 'src' directory
file(GLOB SOURCE_FILES "${SOURCE_DIR}/*.cpp")
file(GLOB SOURCE_FILES_1 "${SOURCE_DIR_1}/*.cpp")
idf_component_register(SRCS ${SOURCE_FILES} ${SOURCE_FILES_1}
INCLUDE_DIRS "./src" "./src/knx"
REQUIRES esp_netif driver esp_timer esp_wifi freertos nvs_flash esp_system)

View File

@ -0,0 +1,315 @@
#ifdef ARDUINO
#include "arduino_platform.h"
#include "knx/bits.h"
#include <Arduino.h>
#ifndef KNX_NO_SPI
#include <SPI.h>
#endif
#ifndef KNX_NO_PRINT
Stream* ArduinoPlatform::SerialDebug = &KNX_DEBUG_SERIAL;
#endif
ArduinoPlatform::ArduinoPlatform() : _knxSerial(nullptr)
{
}
ArduinoPlatform::ArduinoPlatform(HardwareSerial* knxSerial) : _knxSerial(knxSerial)
{
}
void ArduinoPlatform::fatalError()
{
while (true)
{
#ifdef KNX_LED
static const long LED_BLINK_PERIOD = 200;
if ((millis() % LED_BLINK_PERIOD) > (LED_BLINK_PERIOD / 2))
digitalWrite(KNX_LED, HIGH);
else
digitalWrite(KNX_LED, LOW);
#endif
}
}
void ArduinoPlatform::knxUart( HardwareSerial* serial )
{
if (_knxSerial)
closeUart();
_knxSerial = serial;
setupUart();
}
HardwareSerial* ArduinoPlatform::knxUart()
{
return _knxSerial;
}
void ArduinoPlatform::setupUart()
{
_knxSerial->begin(19200, SERIAL_8E1);
while (!_knxSerial)
;
}
void ArduinoPlatform::closeUart()
{
_knxSerial->end();
}
int ArduinoPlatform::uartAvailable()
{
return _knxSerial->available();
}
size_t ArduinoPlatform::writeUart(const uint8_t data)
{
//printHex("<p", &data, 1);
return _knxSerial->write(data);
}
size_t ArduinoPlatform::writeUart(const uint8_t* buffer, size_t size)
{
//printHex("<p", buffer, size);
return _knxSerial->write(buffer, size);
}
int ArduinoPlatform::readUart()
{
int val = _knxSerial->read();
//if(val > 0)
// printHex("p>", (uint8_t*)&val, 1);
return val;
}
size_t ArduinoPlatform::readBytesUart(uint8_t* buffer, size_t length)
{
size_t toRead = length;
uint8_t* pos = buffer;
while (toRead > 0)
{
size_t val = _knxSerial->readBytes(pos, toRead);
pos += val;
toRead -= val;
}
//printHex("p>", buffer, length);
return length;
}
void ArduinoPlatform::flushUart()
{
return _knxSerial->flush();
}
#ifndef KNX_NO_SPI
void ArduinoPlatform::setupSpi()
{
SPI.begin();
SPI.beginTransaction(SPISettings(4000000, MSBFIRST, SPI_MODE0));
}
void ArduinoPlatform::closeSpi()
{
SPI.endTransaction();
SPI.end();
}
int ArduinoPlatform::readWriteSpi(uint8_t* data, size_t len)
{
SPI.transfer(data, len);
return 0;
}
#endif
#ifndef KNX_NO_PRINT
void printUint64(uint64_t value, int base = DEC)
{
char buf[8 * sizeof(uint64_t) + 1];
char* str = &buf[sizeof(buf) - 1];
*str = '\0';
uint64_t n = value;
do
{
char c = n % base;
n /= base;
*--str = c < 10 ? c + '0' : c + 'A' - 10;
} while (n > 0);
print(str);
}
void print(const char* s)
{
ArduinoPlatform::SerialDebug->print(s);
}
void print(char c)
{
ArduinoPlatform::SerialDebug->print(c);
}
void print(unsigned char num)
{
ArduinoPlatform::SerialDebug->print(num);
}
void print(unsigned char num, int base)
{
ArduinoPlatform::SerialDebug->print(num, base);
}
void print(int num)
{
ArduinoPlatform::SerialDebug->print(num);
}
void print(int num, int base)
{
ArduinoPlatform::SerialDebug->print(num, base);
}
void print(unsigned int num)
{
ArduinoPlatform::SerialDebug->print(num);
}
void print(unsigned int num, int base)
{
ArduinoPlatform::SerialDebug->print(num, base);
}
void print(long num)
{
ArduinoPlatform::SerialDebug->print(num);
}
void print(long num, int base)
{
ArduinoPlatform::SerialDebug->print(num, base);
}
void print(unsigned long num)
{
ArduinoPlatform::SerialDebug->print(num);
}
void print(unsigned long num, int base)
{
ArduinoPlatform::SerialDebug->print(num, base);
}
void print(unsigned long long num)
{
printUint64(num);
}
void print(unsigned long long num, int base)
{
printUint64(num, base);
}
void print(double num)
{
ArduinoPlatform::SerialDebug->print(num);
}
void println(const char* s)
{
ArduinoPlatform::SerialDebug->println(s);
}
void println(char c)
{
ArduinoPlatform::SerialDebug->println(c);
}
void println(unsigned char num)
{
ArduinoPlatform::SerialDebug->println(num);
}
void println(unsigned char num, int base)
{
ArduinoPlatform::SerialDebug->println(num, base);
}
void println(int num)
{
ArduinoPlatform::SerialDebug->println(num);
}
void println(int num, int base)
{
ArduinoPlatform::SerialDebug->println(num, base);
}
void println(unsigned int num)
{
ArduinoPlatform::SerialDebug->println(num);
}
void println(unsigned int num, int base)
{
ArduinoPlatform::SerialDebug->println(num, base);
}
void println(long num)
{
ArduinoPlatform::SerialDebug->println(num);
}
void println(long num, int base)
{
ArduinoPlatform::SerialDebug->println(num, base);
}
void println(unsigned long num)
{
ArduinoPlatform::SerialDebug->println(num);
}
void println(unsigned long num, int base)
{
ArduinoPlatform::SerialDebug->println(num, base);
}
void println(unsigned long long num)
{
printUint64(num);
println("");
}
void println(unsigned long long num, int base)
{
printUint64(num, base);
println("");
}
void println(double num)
{
ArduinoPlatform::SerialDebug->println(num);
}
void println(void)
{
ArduinoPlatform::SerialDebug->println();
}
#endif // KNX_NO_PRINT
#endif // ARDUINO

View File

@ -0,0 +1,47 @@
#pragma once
#ifdef ARDUINO
#include "knx/platform.h"
#include "Arduino.h"
#ifndef KNX_DEBUG_SERIAL
#define KNX_DEBUG_SERIAL Serial
#endif
class ArduinoPlatform : public Platform
{
public:
ArduinoPlatform();
ArduinoPlatform(HardwareSerial* knxSerial);
// basic stuff
void fatalError();
//uart
virtual void knxUart( HardwareSerial* serial);
virtual HardwareSerial* knxUart();
virtual void setupUart();
virtual void closeUart();
virtual int uartAvailable();
virtual size_t writeUart(const uint8_t data);
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
void setupSpi() override;
void closeSpi() override;
int readWriteSpi (uint8_t* data, size_t len) override;
#endif
#ifndef KNX_NO_PRINT
static Stream* SerialDebug;
#endif
protected:
HardwareSerial* _knxSerial;
};
#endif // ARDUINO

View File

@ -0,0 +1,587 @@
#ifdef DeviceFamily_CC13X0
#include <cstdio>
#include <cstdarg>
#include <cstring>
#include <cmath>
#include <ti/devices/DeviceFamily.h>
#include DeviceFamily_constructPath(driverlib/sys_ctrl.h)
#include <ti/drivers/GPIO.h>
#include "SEGGER_RTT.h"
#include "Board.h"
#include "knx/bits.h"
#include "cc1310_platform.h"
//#define printf(args...) (SEGGER_RTT_printf(0, args))
//#define PRINT_RTT
#define PRINT_UART
static uint8_t serialNumber[6];
// KNX_FLASH_SIZE shall be defined in CMakeLists.txt for example. It is also used in class Memory in memory.cpp
static uint8_t NVS_buffer[KNX_FLASH_SIZE];
static UART_Handle uart;
static NVS_Handle nvsHandle;
static ClockP_Handle clk0Handle;
static ClockP_Struct clk0Struct;
static volatile uint32_t msCounter = 0;
static void clk0Fxn(uintptr_t arg0)
{
msCounter++;
}
static void setupClock()
{
ClockP_Params clkParams;
ClockP_Params_init(&clkParams);
clkParams.period = 1000 / ClockP_tickPeriod;
clkParams.startFlag = true;
ClockP_construct(&clk0Struct, (ClockP_Fxn)clk0Fxn, 1000 / ClockP_tickPeriod, &clkParams);
clk0Handle = ClockP_handle(&clk0Struct);
}
static void setupGPIO()
{
/* Configure the LED and button pins */
GPIO_setConfig(Board_GPIO_LED0, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
GPIO_setConfig(Board_GPIO_LED1, GPIO_CFG_OUT_STD | GPIO_CFG_OUT_LOW);
GPIO_setConfig(Board_GPIO_BUTTON0, GPIO_CFG_IN_PU | GPIO_CFG_IN_INT_FALLING);
GPIO_setConfig(Board_GPIO_BUTTON1, GPIO_CFG_IN_PU | GPIO_CFG_IN_INT_FALLING);
}
static void setupUART()
{
UART_Params uartParams;
UART_Params_init(&uartParams);
uartParams.writeDataMode = UART_DATA_BINARY;
uartParams.readDataMode = UART_DATA_BINARY;
uartParams.readReturnMode = UART_RETURN_FULL;
uartParams.readEcho = UART_ECHO_OFF;
uartParams.baudRate = 115200;
uart = UART_open(Board_UART0, &uartParams);
if (uart == NULL)
{
while (true)
{}
}
}
static void setupNVS()
{
NVS_Params nvsParams;
NVS_Params_init(&nvsParams);
nvsHandle = NVS_open(Board_NVSINTERNAL, &nvsParams);
if (nvsHandle == NULL)
{
println("NVS_open() failed.");
return;
}
NVS_Attrs attrs;
NVS_getAttrs(nvsHandle, &attrs);
print("NVS flash size: ");
println((int)attrs.regionSize);
print("NVS flash sector size: ");
println((int)attrs.sectorSize);
if (GPIO_read(Board_GPIO_BUTTON1) == 0)
{
println("Button1 is pressed. Erasing flash...");
int_fast16_t result = NVS_erase(nvsHandle, 0, attrs.regionSize);
if (result != NVS_STATUS_SUCCESS)
{
print("Error erasing NVS, result: ");
println(result);
}
else
{
println("NVS successfully erased.");
}
}
}
void sleep(uint32_t sec)
{
ClockP_sleep(sec);
}
void usleep(uint32_t usec)
{
ClockP_usleep(usec);
}
uint32_t millis()
{
// we use our own ms clock because the Os tick counter has counts 10us ticks and following calculation would not wrap correctly at 32bit boundary
//return Clock_getTicks() * (uint64_t) Clock_tickPeriod / 1000; // rtos
//return ClockP_getTicks( * (uint64_t) Clock_tickPeriod / 1000); //nortos
return msCounter;
}
void delay(uint32_t ms)
{
ClockP_usleep(ms * 1000);
//sleep(ms * (1000 / ClockP_tickPeriod)); //rtos
//sleepTicks(millis * 1000ULL / ClockP_tickPeriod); //nortos
}
void delayMicroseconds (unsigned int howLong)
{
ClockP_usleep(howLong);
}
#ifndef KNX_NO_PRINT
size_t write(uint8_t c)
{
#if defined(PRINT_UART)
uint8_t buffer[1] = {c};
return UART_write(uart, buffer, sizeof(buffer));
#elif defined (PRINT_RTT)
return SEGGER_RTT_PutChar(0, (char)c);
#else
return 1;
#endif
}
#if 0
size_t write(const uint8_t* buffer, size_t size)
{
size_t n = 0;
while (size--)
{
if (write(*buffer++))
{
n++;
}
else
{
break;
}
}
return n;
}
#else
size_t write(const uint8_t* buffer, size_t size)
{
#if defined(PRINT_UART)
return UART_write(uart, buffer, size);
#elif defined (PRINT_RTT)
return SEGGER_RTT_Write(0, buffer, size);
#else
return size;
#endif
}
#endif
size_t write(const char* buffer, size_t size)
{
return write((const uint8_t*)buffer, size);
}
void print(const char* s)
{
if (s == NULL)
{
return;
}
write(s, strlen(s));
}
void print(char c)
{
write(c);
}
void printUint64(uint64_t value, int base = DEC)
{
char buf[8 * sizeof(uint64_t) + 1];
char* str = &buf[sizeof(buf) - 1];
*str = '\0';
uint64_t n = value;
do
{
char c = n % base;
n /= base;
*--str = c < 10 ? c + '0' : c + 'A' - 10;
} while (n > 0);
print(str);
}
void print(long long num, int base)
{
if (base == 0)
{
write(num);
return;
}
else if (base == 10)
{
if (num < 0)
{
print('-');
num = -num;
printUint64(num, 10);
return;
}
printUint64(num, 10);
return;
}
else
{
printUint64(num, base);
return;
}
}
void print(unsigned long long num, int base)
{
if (base == 0)
{
write(num);
return;
}
else
{
printUint64(num, base);
return;
}
}
void print(unsigned char num, int base)
{
print((unsigned long long)num, base);
}
void print(int num, int base)
{
print((long long)num, base);
}
void print(unsigned int num, int base)
{
print((unsigned long long)num, base);
}
void print(long num, int base)
{
print((long long)num, base);
}
void print(unsigned long num, int base)
{
print((unsigned long long)num, base);
}
void printFloat(double number, uint8_t digits)
{
if (std::isnan(number))
{
print("nan");
return;
}
if (std::isinf(number))
{
print("inf");
return;
}
if (number > 4294967040.0)
{
print("ovf"); // constant determined empirically
return;
}
if (number < -4294967040.0)
{
print("ovf"); // constant determined empirically
return;
}
// Handle negative numbers
if (number < 0.0)
{
print('-');
number = -number;
}
// Round correctly so that print(1.999, 2) prints as "2.00"
double rounding = 0.5;
for (uint8_t i = 0; i < digits; ++i)
rounding /= 10.0;
number += rounding;
// Extract the integer part of the number and print it
unsigned long int_part = (unsigned long)number;
double remainder = number - (double)int_part;
printUint64(int_part);
// Print the decimal point, but only if there are digits beyond
if (digits > 0)
{
print('.');
}
// Extract digits from the remainder one at a time
while (digits-- > 0)
{
remainder *= 10.0;
unsigned int toPrint = (unsigned int)(remainder);
printUint64(toPrint);
remainder -= toPrint;
}
}
void print(double num, int digits = 2)
{
printFloat(num, digits);
}
void println(void)
{
print("\r\n");
}
void println(const char* s)
{
print(s);
println();
}
void println(char c)
{
print(c);
println();
}
void println(unsigned char num, int base)
{
print(num, base);
println();
}
void println(int num, int base)
{
print(num, base);
println();
}
void println(unsigned int num, int base)
{
print(num, base);
println();
}
void println(long num, int base)
{
print(num, base);
println();
}
void println(unsigned long num, int base)
{
print(num, base);
println();
}
void println(unsigned long long num, int base)
{
printUint64(num, base);
println();
}
void println(double num, int digits = 2)
{
print(num, digits);
println();
}
void println(double num)
{
// default: print 10 digits
println(num, 10);
}
#endif // KNX_NO_PRINT
uint32_t digitalRead(uint32_t dwPin)
{
print("ignoring digitalRead: pin: ");
print(dwPin);
println(", returning 0");
return 0;
}
void digitalWrite(unsigned long pin, unsigned long value)
{
if (pin == Board_GPIO_LED0)
{
if (value > 0)
{
GPIO_write(Board_GPIO_LED0, Board_GPIO_LED_ON);
}
else
{
GPIO_write(Board_GPIO_LED0, Board_GPIO_LED_OFF);
}
}
else
{
print("dummy digitalWrite: pin: ");
print(pin);
print(", value: ");
println(value, HEX);
}
}
void pinMode(unsigned long pin, unsigned long mode)
{
print("ignoring pinMode: pin: ");
print(pin);
print(", mode: ");
println(mode, HEX);
}
typedef void (*IsrFuncPtr)();
static IsrFuncPtr gpioCallback;
static void gpioButtonFxn0(uint_least8_t index)
{
gpioCallback();
}
void attachInterrupt(uint32_t pin, IsrFuncPtr callback, uint32_t mode)
{
if (pin == Board_GPIO_BUTTON0)
{
gpioCallback = callback;
/* install Button callback */
GPIO_setCallback(Board_GPIO_BUTTON0, gpioButtonFxn0);
/* Enable interrupts */
GPIO_enableInt(Board_GPIO_BUTTON0);
}
else
{
print("dummy attachInterrupt: pin: ");
print(pin);
print(", mode: ");
println(mode, HEX);
}
}
CC1310Platform::CC1310Platform()
{
// build serialNumber from IEEE MAC Address (MAC is 8 bytes, serialNumber 6 bytes only)
*(uint32_t*)(serialNumber + 2) = HWREG(FCFG1_BASE + FCFG1_O_MAC_15_4_0) ^ HWREG(FCFG1_BASE + FCFG1_O_MAC_15_4_1); // make a 6 byte hash from 8 bytes
}
CC1310Platform::~CC1310Platform()
{
}
void CC1310Platform::init()
{
// TI Drivers init
// According to SDK docs it is safe to call them AFTER NoRTOS_Start()
// If RTOS is used and multiple thread use the same driver, then the init shall be performed before BIOS_Start()
GPIO_init();
UART_init();
NVS_init();
// Init GPIO
setupGPIO();
// Init UART
setupUART();
// tick Period on this controller 10us so we use our own millisecond clock
setupClock();
// Init flash
setupNVS();
}
uint8_t* CC1310Platform::getEepromBuffer(uint32_t size)
{
if (size > KNX_FLASH_SIZE)
{
fatalError();
}
NVS_read(nvsHandle, 0, (void*) NVS_buffer, size);
for (int i = 0; i < size; i++)
{
if (NVS_buffer[i] != 0)
{
return NVS_buffer;
}
}
memset(NVS_buffer, 0xff, size);
return NVS_buffer;
}
void CC1310Platform::commitToEeprom()
{
println("CC1310Platform::commitToEeprom() ...");
int_fast16_t result = NVS_write(nvsHandle, 0, (void*)NVS_buffer, KNX_FLASH_SIZE, NVS_WRITE_ERASE | NVS_WRITE_POST_VERIFY);
if (result != NVS_STATUS_SUCCESS)
{
print("Error writing to NVS, result: ");
println(result);
}
else
{
println("NVS successfully written");
}
delay(500);
}
void CC1310Platform::restart()
{
println("System restart in 500ms.");
delay(500);
SysCtrlSystemReset();
// Should neber be reached!
fatalError();
}
void CC1310Platform::fatalError()
{
println("A fatal error occured. Stopped.");
while (true)
{
/* Turn on user LED */
GPIO_write(Board_GPIO_LED0, Board_GPIO_LED_OFF);
GPIO_write(Board_GPIO_LED1, Board_GPIO_LED_ON);
delay(500);
GPIO_write(Board_GPIO_LED0, Board_GPIO_LED_ON);
GPIO_write(Board_GPIO_LED1, Board_GPIO_LED_OFF);
delay(500);
}
}
#endif // DeviceFamily_CC13X0

View File

@ -0,0 +1,27 @@
#pragma once
#ifdef DeviceFamily_CC13X0
#include <ti/drivers/NVS.h>
#include <ti/drivers/UART.h>
#include <ti/drivers/dpl/ClockP.h>
#include "knx/platform.h"
class CC1310Platform : public Platform
{
public:
CC1310Platform();
virtual ~CC1310Platform();
void init();
// basic stuff
void restart() final;
void fatalError() final;
uint8_t* getEepromBuffer(uint32_t size) final;
void commitToEeprom() final;
};
#endif //DeviceFamily_CC13X0

View File

@ -0,0 +1,443 @@
#ifndef ARDUINO
#ifdef ESP_PLATFORM
// esp32_idf_platform.cpp
#include <esp_system.h>
#include <esp_mac.h>
#include "esp32_idf_platform.h"
#include "esp_log.h"
#include "knx/bits.h"
#include "nvs.h"
#include <esp_timer.h>
static const char* KTAG = "KNX_LIB";
Esp32IdfPlatform::Esp32IdfPlatform(uart_port_t uart_num)
: _uart_num(uart_num)
{
// Set the memory type to use our NVS-based EEPROM emulation
_memoryType = Eeprom;
}
Esp32IdfPlatform::~Esp32IdfPlatform()
{
if (_sock != -1)
{
closeMultiCast();
}
if (_uart_installed)
{
closeUart();
}
if (_eeprom_buffer)
{
free(_eeprom_buffer);
}
if (_nvs_handle)
{
nvs_close(_nvs_handle);
}
}
void Esp32IdfPlatform::knxUartPins(int8_t rxPin, int8_t txPin)
{
_rxPin = rxPin;
_txPin = txPin;
}
void Esp32IdfPlatform::knxUartBaudRate(uint32_t baudRate)
{
_baudRate = baudRate;
ESP_LOGI(KTAG, "UART baud rate set to %lu", _baudRate);
}
void Esp32IdfPlatform::setNetif(esp_netif_t* netif)
{
_netif = netif;
}
void Esp32IdfPlatform::fatalError()
{
ESP_LOGE(KTAG, "FATAL ERROR. System halted.");
// Loop forever to halt the system
while (1)
{
vTaskDelay(pdMS_TO_TICKS(1000));
}
}
// ESP specific uart handling with pins
void Esp32IdfPlatform::setupUart()
{
if (_uart_installed)
return;
uart_config_t uart_config;
memset(&uart_config, 0, sizeof(uart_config));
uart_config.baud_rate = _baudRate; // Use configurable baud rate
uart_config.data_bits = UART_DATA_8_BITS;
uart_config.parity = UART_PARITY_EVEN;
uart_config.stop_bits = UART_STOP_BITS_1;
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
uart_config.source_clk = UART_SCLK_DEFAULT;
ESP_ERROR_CHECK(uart_driver_install(_uart_num, 256 * 2, 0, 0, NULL, 0));
ESP_ERROR_CHECK(uart_param_config(_uart_num, &uart_config));
ESP_ERROR_CHECK(uart_set_pin(_uart_num, _txPin, _rxPin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE));
_uart_installed = true;
ESP_LOGI(KTAG, "UART initialized with baud rate %lu", _baudRate);
}
void Esp32IdfPlatform::closeUart()
{
if (!_uart_installed)
return;
uart_driver_delete(_uart_num);
_uart_installed = false;
}
int Esp32IdfPlatform::uartAvailable()
{
if (!_uart_installed)
return 0;
size_t length = 0;
ESP_ERROR_CHECK(uart_get_buffered_data_len(_uart_num, &length));
return length;
}
size_t Esp32IdfPlatform::writeUart(const uint8_t data)
{
if (!_uart_installed)
return 0;
return uart_write_bytes(_uart_num, &data, 1);
}
size_t Esp32IdfPlatform::writeUart(const uint8_t* buffer, size_t size)
{
if (!_uart_installed)
return 0;
return uart_write_bytes(_uart_num, buffer, size);
}
int Esp32IdfPlatform::readUart()
{
if (!_uart_installed)
return -1;
uint8_t data;
if (uart_read_bytes(_uart_num, &data, 1, pdMS_TO_TICKS(20)) > 0)
{
return data;
}
return -1;
}
size_t Esp32IdfPlatform::readBytesUart(uint8_t* buffer, size_t length)
{
if (!_uart_installed)
return 0;
return uart_read_bytes(_uart_num, buffer, length, pdMS_TO_TICKS(100));
}
void Esp32IdfPlatform::flushUart()
{
if (!_uart_installed)
return;
ESP_ERROR_CHECK(uart_flush(_uart_num));
}
uint32_t Esp32IdfPlatform::currentIpAddress()
{
if (!_netif)
return 0;
esp_netif_ip_info_t ip_info;
esp_netif_get_ip_info(_netif, &ip_info);
return ip_info.ip.addr;
}
uint32_t Esp32IdfPlatform::currentSubnetMask()
{
if (!_netif)
return 0;
esp_netif_ip_info_t ip_info;
esp_netif_get_ip_info(_netif, &ip_info);
return ip_info.netmask.addr;
}
uint32_t Esp32IdfPlatform::currentDefaultGateway()
{
if (!_netif)
return 0;
esp_netif_ip_info_t ip_info;
esp_netif_get_ip_info(_netif, &ip_info);
return ip_info.gw.addr;
}
void Esp32IdfPlatform::macAddress(uint8_t* addr)
{
if (!_netif)
return;
esp_netif_get_mac(_netif, addr);
}
uint32_t Esp32IdfPlatform::uniqueSerialNumber()
{
uint8_t mac[6];
esp_efuse_mac_get_default(mac);
uint64_t chipid = 0;
for (int i = 0; i < 6; i++)
{
chipid |= ((uint64_t)mac[i] << (i * 8));
}
uint32_t upperId = (chipid >> 32) & 0xFFFFFFFF;
uint32_t lowerId = (chipid & 0xFFFFFFFF);
return (upperId ^ lowerId);
}
void Esp32IdfPlatform::restart()
{
ESP_LOGI(KTAG, "Restarting system...");
esp_restart();
}
void Esp32IdfPlatform::setupMultiCast(uint32_t addr, uint16_t port)
{
_multicast_addr = addr;
_multicast_port = port;
_sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_IP);
if (_sock < 0)
{
ESP_LOGE(KTAG, "Failed to create socket. Errno: %d", errno);
return;
}
struct sockaddr_in saddr;
memset(&saddr, 0, sizeof(saddr));
saddr.sin_family = AF_INET;
saddr.sin_port = htons(port);
saddr.sin_addr.s_addr = htonl(INADDR_ANY);
if (bind(_sock, (struct sockaddr*)&saddr, sizeof(struct sockaddr_in)) < 0)
{
ESP_LOGE(KTAG, "Failed to bind socket. Errno: %d", errno);
close(_sock);
_sock = -1;
return;
}
struct ip_mreq imreq;
memset(&imreq, 0, sizeof(imreq));
imreq.imr_interface.s_addr = IPADDR_ANY;
imreq.imr_multiaddr.s_addr = addr;
if (setsockopt(_sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imreq, sizeof(struct ip_mreq)) < 0)
{
ESP_LOGE(KTAG, "Failed to join multicast group. Errno: %d", errno);
close(_sock);
_sock = -1;
return;
}
ESP_LOGI(KTAG, "Successfully joined multicast group on port %d", port);
}
void Esp32IdfPlatform::closeMultiCast()
{
if (_sock != -1)
{
close(_sock);
_sock = -1;
}
}
bool Esp32IdfPlatform::sendBytesMultiCast(uint8_t* buffer, uint16_t len)
{
if (_sock < 0)
return false;
struct sockaddr_in dest_addr = {};
dest_addr.sin_family = AF_INET;
dest_addr.sin_port = htons(_multicast_port);
dest_addr.sin_addr.s_addr = _multicast_addr;
int sent_len = sendto(_sock, buffer, len, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr));
if (sent_len < 0)
{
ESP_LOGE(KTAG, "sendBytesMultiCast failed. Errno: %d", errno);
return false;
}
return sent_len == len;
}
int Esp32IdfPlatform::readBytesMultiCast(uint8_t* buffer, uint16_t maxLen, uint32_t& src_addr, uint16_t& src_port)
{
if (_sock < 0)
return 0;
socklen_t socklen = sizeof(_remote_addr);
int len = recvfrom(_sock, buffer, maxLen, 0, (struct sockaddr*)&_remote_addr, &socklen);
if (len <= 0)
{
return 0; // No data or error
}
src_addr = _remote_addr.sin_addr.s_addr;
src_port = ntohs(_remote_addr.sin_port);
return len;
}
bool Esp32IdfPlatform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len)
{
if (_sock < 0)
return false;
struct sockaddr_in dest_addr;
dest_addr.sin_family = AF_INET;
if (addr == 0)
{ // If address is 0, use the address from the last received packet
dest_addr.sin_addr.s_addr = _remote_addr.sin_addr.s_addr;
}
else
{
dest_addr.sin_addr.s_addr = addr;
}
if (port == 0)
{ // If port is 0, use the port from the last received packet
dest_addr.sin_port = _remote_addr.sin_port;
}
else
{
dest_addr.sin_port = htons(port);
}
if (sendto(_sock, buffer, len, 0, (struct sockaddr*)&dest_addr, sizeof(dest_addr)) < 0)
{
ESP_LOGE(KTAG, "sendBytesUniCast failed. Errno: %d", errno);
return false;
}
return true;
}
uint8_t* Esp32IdfPlatform::getEepromBuffer(uint32_t size)
{
if (_eeprom_buffer && _eeprom_size == size)
{
return _eeprom_buffer;
}
if (_eeprom_buffer)
{
free(_eeprom_buffer);
_eeprom_buffer = nullptr;
}
_eeprom_size = size;
_eeprom_buffer = (uint8_t*)malloc(size);
if (!_eeprom_buffer)
{
ESP_LOGE(KTAG, "Failed to allocate EEPROM buffer");
fatalError();
return nullptr;
}
esp_err_t err = nvs_flash_init();
if (err == ESP_ERR_NVS_NO_FREE_PAGES || err == ESP_ERR_NVS_NEW_VERSION_FOUND)
{
ESP_ERROR_CHECK(nvs_flash_erase());
err = nvs_flash_init();
}
ESP_ERROR_CHECK(err);
err = nvs_open(_nvs_namespace, NVS_READWRITE, &_nvs_handle);
if (err != ESP_OK)
{
ESP_LOGE(KTAG, "Error opening NVS handle: %s", esp_err_to_name(err));
free(_eeprom_buffer);
_eeprom_buffer = nullptr;
fatalError();
return nullptr;
}
size_t required_size = size;
err = nvs_get_blob(_nvs_handle, _nvs_key, _eeprom_buffer, &required_size);
if (err != ESP_OK || required_size != size)
{
if (err == ESP_ERR_NVS_NOT_FOUND)
{
ESP_LOGI(KTAG, "No previous EEPROM data found in NVS. Initializing fresh buffer.");
}
else
{
ESP_LOGW(KTAG, "NVS get blob failed (%s) or size mismatch. Initializing fresh buffer.", esp_err_to_name(err));
}
memset(_eeprom_buffer, 0xFF, size);
}
else
{
ESP_LOGI(KTAG, "Successfully loaded %d bytes from NVS into EEPROM buffer.", required_size);
}
return _eeprom_buffer;
}
void Esp32IdfPlatform::commitToEeprom()
{
if (!_eeprom_buffer || !_nvs_handle)
{
ESP_LOGE(KTAG, "EEPROM not initialized, cannot commit.");
return;
}
esp_err_t err = nvs_set_blob(_nvs_handle, _nvs_key, _eeprom_buffer, _eeprom_size);
if (err != ESP_OK)
{
ESP_LOGE(KTAG, "Failed to set NVS blob: %s", esp_err_to_name(err));
return;
}
err = nvs_commit(_nvs_handle);
if (err != ESP_OK)
{
ESP_LOGE(KTAG, "Failed to commit NVS: %s", esp_err_to_name(err));
}
else
{
ESP_LOGI(KTAG, "Committed %" PRIu32 " bytes to NVS.", _eeprom_size);
}
}
uint32_t millis()
{
// esp_timer_get_time() returns microseconds, so we divide by 1000 for milliseconds.
// Cast to uint32_t to match the Arduino function signature.
return (uint32_t)(esp_timer_get_time() / 1000);
}
// Internal wrapper function to bridge Arduino-style ISR to ESP-IDF
static void IRAM_ATTR isr_wrapper(void* arg)
{
IsrFuncPtr fn = (IsrFuncPtr)arg;
fn(); // call the original ISR
}
// Implement attachInterrupt arduino like in ESP IDF
void attachInterrupt(uint32_t pin, IsrFuncPtr callback, uint32_t mode)
{
gpio_config_t io_conf = {
.pin_bit_mask = (1ULL << pin),
.mode = GPIO_MODE_INPUT,
.pull_up_en = GPIO_PULLUP_ENABLE,
.pull_down_en = GPIO_PULLDOWN_DISABLE,
.intr_type = (gpio_int_type_t)mode
};
ESP_ERROR_CHECK(gpio_config(&io_conf));
ESP_ERROR_CHECK(gpio_install_isr_service(0));
// Add ISR using the wrapper and pass original function as argument
ESP_ERROR_CHECK(gpio_isr_handler_add((gpio_num_t)pin, isr_wrapper, (void*)callback));
}
#endif // ESP_PLATFORM
#endif // !ARDUINO

View File

@ -0,0 +1,89 @@
#ifndef ARDUINO
#ifdef ESP_PLATFORM
// esp_idf_platform.h
#pragma once
#include "driver/uart.h"
#include "esp_netif.h"
#include "esp_system.h"
#include "lwip/sockets.h"
#include "nvs_flash.h"
#include "knx/platform.h"// Include the provided base class
class Esp32IdfPlatform : public Platform
{
public:
Esp32IdfPlatform(uart_port_t uart_num = UART_NUM_1);
~Esp32IdfPlatform();
// uart
void knxUartPins(int8_t rxPin, int8_t txPin);
void knxUartBaudRate(uint32_t baudRate); // Add baud rate configuration
// Call this after WiFi/Ethernet has started and received an IP.
void setNetif(esp_netif_t* netif);
// --- Overridden Virtual Functions ---
// ip stuff
uint32_t currentIpAddress() override;
uint32_t currentSubnetMask() override;
uint32_t currentDefaultGateway() override;
void macAddress(uint8_t* addr) override;
// unique serial number
uint32_t uniqueSerialNumber() override;
// basic stuff (pure virtual in base)
void restart() override;
void fatalError() override;
// multicast
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, 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;
// UART
void setupUart() override;
void closeUart() override;
int uartAvailable() override;
size_t writeUart(const uint8_t data) override;
size_t writeUart(const uint8_t* buffer, size_t size) override;
int readUart() override;
size_t readBytesUart(uint8_t* buffer, size_t length) override;
void flushUart() override;
// Memory (EEPROM emulation via NVS)
// We override these two functions to provide the low-level storage mechanism.
// The base Platform class will use them when _memoryType is Eeprom.
uint8_t* getEepromBuffer(uint32_t size) override;
void commitToEeprom() override;
private:
// Network
esp_netif_t* _netif = nullptr;
int _sock = -1;
struct sockaddr_in _remote_addr;
uint32_t _multicast_addr = 0;
uint16_t _multicast_port = 0;
// UART
uart_port_t _uart_num;
int8_t _rxPin = -1;
int8_t _txPin = -1;
uint32_t _baudRate = 19200; // Default baud rate, can be changed
bool _uart_installed = false;
// NVS (for EEPROM emulation)
nvs_handle_t _nvs_handle;
uint8_t* _eeprom_buffer = nullptr;
uint32_t _eeprom_size = 0;
const char* _nvs_namespace = "eeprom";
const char* _nvs_key = "eeprom";
};
#endif
#endif

View File

@ -0,0 +1,184 @@
#include "esp32_platform.h"
#ifdef ARDUINO_ARCH_ESP32
#include <Arduino.h>
#include <EEPROM.h>
#include "knx/bits.h"
#ifndef KNX_SERIAL
#define KNX_SERIAL Serial1
#pragma warn "KNX_SERIAL not defined, using Serial1"
#endif
#ifdef KNX_IP_LAN
#include "ETH.h"
#define KNX_NETIF ETH
#else // KNX_IP_WIFI
#include <WiFi.h>
#define KNX_NETIF WiFi
#endif
Esp32Platform::Esp32Platform()
#ifndef KNX_NO_DEFAULT_UART
: ArduinoPlatform(&KNX_SERIAL)
#endif
{
#ifdef KNX_UART_RX_PIN
_rxPin = KNX_UART_RX_PIN;
#endif
#ifdef KNX_UART_TX_PIN
_txPin = KNX_UART_TX_PIN;
#endif
}
Esp32Platform::Esp32Platform(HardwareSerial* s) : ArduinoPlatform(s)
{
}
void Esp32Platform::knxUartPins(int8_t rxPin, int8_t txPin)
{
_rxPin = rxPin;
_txPin = txPin;
}
// ESP specific uart handling with pins
void Esp32Platform::setupUart()
{
_knxSerial->begin(19200, SERIAL_8E1, _rxPin, _txPin);
while (!_knxSerial)
;
}
uint32_t Esp32Platform::currentIpAddress()
{
return KNX_NETIF.localIP();
}
uint32_t Esp32Platform::currentSubnetMask()
{
return KNX_NETIF.subnetMask();
}
uint32_t Esp32Platform::currentDefaultGateway()
{
return KNX_NETIF.gatewayIP();
}
void Esp32Platform::macAddress(uint8_t* addr)
{
KNX_NETIF.macAddress(addr);
}
uint32_t Esp32Platform::uniqueSerialNumber()
{
uint64_t chipid = ESP.getEfuseMac();
uint32_t upperId = (chipid >> 32) & 0xFFFFFFFF;
uint32_t lowerId = (chipid & 0xFFFFFFFF);
return (upperId ^ lowerId);
}
void Esp32Platform::restart()
{
println("restart");
ESP.restart();
}
void Esp32Platform::setupMultiCast(uint32_t addr, uint16_t port)
{
IPAddress mcastaddr(htonl(addr));
KNX_DEBUG_SERIAL.printf("setup multicast addr: %s port: %d ip: %s\n", mcastaddr.toString().c_str(), port,
KNX_NETIF.localIP().toString().c_str());
uint8_t result = _udp.beginMulticast(mcastaddr, port);
KNX_DEBUG_SERIAL.printf("multicast setup result %d\n", result);
}
void Esp32Platform::closeMultiCast()
{
_udp.stop();
}
bool Esp32Platform::sendBytesMultiCast(uint8_t* buffer, uint16_t len)
{
//printHex("<- ",buffer, len);
_udp.beginMulticastPacket();
_udp.write(buffer, len);
_udp.endPacket();
return true;
}
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)
{
println("Unexpected UDP data packet length - drop packet");
for (size_t i = 0; i < len; i++)
_udp.read();
return 0;
}
_udp.read(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));
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");
return true;
}
uint8_t* Esp32Platform::getEepromBuffer(uint32_t size)
{
uint8_t* eepromptr = EEPROM.getDataPtr();
if (eepromptr == nullptr)
{
EEPROM.begin(size);
eepromptr = EEPROM.getDataPtr();
}
return eepromptr;
}
void Esp32Platform::commitToEeprom()
{
EEPROM.getDataPtr(); // trigger dirty flag in EEPROM lib to make sure data will be written to flash
EEPROM.commit();
}
#endif

View File

@ -0,0 +1,54 @@
#ifdef ARDUINO_ARCH_ESP32
#include "arduino_platform.h"
#include <WiFiUdp.h>
class Esp32Platform : public ArduinoPlatform
{
public:
Esp32Platform();
Esp32Platform(HardwareSerial* s);
// uart
void knxUartPins(int8_t rxPin, int8_t txPin);
void setupUart() override;
// ip stuff
uint32_t currentIpAddress() override;
uint32_t currentSubnetMask() override;
uint32_t currentDefaultGateway() override;
void macAddress(uint8_t* addr) override;
// unique serial number
uint32_t uniqueSerialNumber() override;
// basic stuff
void restart();
//multicast
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, 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;
//memory
uint8_t* getEepromBuffer(uint32_t size);
void commitToEeprom();
protected:
IPAddress _remoteIP;
protected:
uint16_t _remotePort;
private:
WiFiUDP _udp;
int8_t _rxPin = -1;
int8_t _txPin = -1;
};
#endif

View File

@ -0,0 +1,135 @@
#include "esp_platform.h"
#ifdef ARDUINO_ARCH_ESP8266
#include <user_interface.h>
#include <Arduino.h>
#include <EEPROM.h>
#include "knx/bits.h"
#ifndef KNX_SERIAL
#define KNX_SERIAL Serial
#endif
EspPlatform::EspPlatform()
#ifndef KNX_NO_DEFAULT_UART
: ArduinoPlatform(&KNX_SERIAL)
#endif
{
}
EspPlatform::EspPlatform( HardwareSerial* s) : ArduinoPlatform(s)
{
}
uint32_t EspPlatform::currentIpAddress()
{
return WiFi.localIP();
}
uint32_t EspPlatform::currentSubnetMask()
{
return WiFi.subnetMask();
}
uint32_t EspPlatform::currentDefaultGateway()
{
return WiFi.gatewayIP();
}
void EspPlatform::macAddress(uint8_t* addr)
{
wifi_get_macaddr(STATION_IF, addr);
}
uint32_t EspPlatform::uniqueSerialNumber()
{
return ESP.getChipId();
}
void EspPlatform::restart()
{
println("restart");
ESP.reset();
}
void EspPlatform::setupMultiCast(uint32_t addr, uint16_t port)
{
_multicastAddr = htonl(addr);
_multicastPort = port;
IPAddress mcastaddr(_multicastAddr);
KNX_DEBUG_SERIAL.printf("setup multicast addr: %s port: %d ip: %s\n", mcastaddr.toString().c_str(), port,
WiFi.localIP().toString().c_str());
uint8 result = _udp.beginMulticast(WiFi.localIP(), mcastaddr, port);
KNX_DEBUG_SERIAL.printf("multicast setup result %d\n", result);
}
void EspPlatform::closeMultiCast()
{
_udp.stop();
}
bool EspPlatform::sendBytesMultiCast(uint8_t* buffer, uint16_t len)
{
//printHex("<- ",buffer, len);
_udp.beginPacketMulticast(_multicastAddr, _multicastPort, WiFi.localIP());
_udp.write(buffer, len);
_udp.endPacket();
return true;
}
int EspPlatform::readBytesMultiCast(uint8_t* buffer, uint16_t maxLen)
{
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();
}
_udp.read(buffer, len);
//printHex("-> ", buffer, len);
return len;
}
bool EspPlatform::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)
{
_udp.write(buffer, len);
if (_udp.endPacket() == 0)
println("sendBytesUniCast endPacket fail");
}
else
println("sendBytesUniCast beginPacket fail");
return true;
}
uint8_t* EspPlatform::getEepromBuffer(uint32_t size)
{
uint8_t* eepromptr = EEPROM.getDataPtr();
if (eepromptr == nullptr)
{
EEPROM.begin(size);
eepromptr = EEPROM.getDataPtr();
}
return eepromptr;
}
void EspPlatform::commitToEeprom()
{
EEPROM.commit();
}
#endif

View File

@ -0,0 +1,43 @@
#ifdef ARDUINO_ARCH_ESP8266
#include "arduino_platform.h"
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
class EspPlatform : public ArduinoPlatform
{
public:
EspPlatform();
EspPlatform(HardwareSerial* s);
// ip stuff
uint32_t currentIpAddress() override;
uint32_t currentSubnetMask() override;
uint32_t currentDefaultGateway() override;
void macAddress(uint8_t* addr) override;
// unique serial number
uint32_t uniqueSerialNumber() override;
// basic stuff
void restart();
//multicast
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;
//unicast
bool sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) override;
//memory
uint8_t* getEepromBuffer(uint32_t size);
void commitToEeprom();
private:
WiFiUDP _udp;
uint32_t _multicastAddr;
uint16_t _multicastPort;
};
#endif

248
components/knx/src/knx.h Normal file
View File

@ -0,0 +1,248 @@
#pragma once
/** \page Classdiagramm KNX device
* This diagramm shows the most important classes of a normal KNX device.
@startuml
skinparam monochrome true
skinparam componentStyle uml2
package "knx" {
class BusAccessUnit [[class_bus_access_unit.html]]
class DeviceObject [[class_device_object.html]]
class BauSystemBDevice [[class_bau_system_b.html]]
BusAccessUnit<|--BauSystemB
BauSystemB<|--BauSystemBDevice
class ApplicationProgramObject [[class_application_program_object.html]]
BauSystemB*--ApplicationProgramObject
DeviceObject--*BauSystemB
class AddressTableObject [[class_address_table_object.html]]
BauSystemBDevice*--AddressTableObject
class AssociationTableObject [[class_association_table_object.html]]
BauSystemBDevice*--AssociationTableObject
class GroupObjectTableObject [[class_group_object_table_object.html]]
BauSystemBDevice*--GroupObjectTableObject
class GroupObject [[class_group_object.html]]
GroupObject<--GroupObjectTableObject
GroupObjectTableObject<--GroupObject
class ApplicationLayer [[class_application_layer.html]]
package knx-data-secure
{
class SecureApplicationLayer [[class_secure_application_layer.html]]
class SecurityInterfaceObject [[class_security_interface_object.html]]
SecureApplicationLayer-->SecurityInterfaceObject
SecurityInterfaceObject-->SecureApplicationLayer
BauSystemBDevice*--"SecurityInterfaceObject"
BauSystemBDevice*--"SecureApplicationLayer"
}
BauSystemBDevice*--"ApplicationLayer"
ApplicationLayer<|--SecureApplicationLayer
SecureApplicationLayer--*BauSystemBDevice
class TransportLayer [[class_transport_layer.html]]
TransportLayer--*BauSystemBDevice
class NetworkLayerDevice [[class_network_layer.html]]
NetworkLayerDevice--*BauSystemBDevice
class NetworkLayerEntity [[class_network_layer_entity.html]]
NetworkLayerEntity--*NetworkLayerDevice
class DataLinkLayer [[class_data_link_layer.html]]
ApplicationLayer-->SecureApplicationLayer
SecureApplicationLayer-->ApplicationLayer
ApplicationLayer-->BusAccessUnit
ApplicationLayer-->TransportLayer
SecureApplicationLayer-->TransportLayer
TransportLayer-->ApplicationLayer
TransportLayer-->SecureApplicationLayer
TransportLayer-->NetworkLayerDevice
NetworkLayerDevice-->TransportLayer
NetworkLayerEntity-->DataLinkLayer
DataLinkLayer-->NetworkLayerEntity
TransportLayer-->AddressTableObject
SecureApplicationLayer-->AddressTableObject
SecureApplicationLayer-->DeviceObject
DataLinkLayer-->DeviceObject
ApplicationLayer-->AssociationTableObject
class Dpt [[class_dpt.html]]
GroupObject->Dpt
package knx-ip
{
class IpDataLinkLayer [[class_ip_data_link_layer.html]]
IpDataLinkLayer--|>DataLinkLayer
class Bau57B0 [[class_bau57_b0.html]]
Bau57B0--|>BauSystemBDevice
Bau57B0*--IpDataLinkLayer
class IpParameterObject [[class_ip_parameter_object.html]]
IpParameterObject-->DeviceObject
Bau57B0*--IpParameterObject
IpDataLinkLayer-->IpParameterObject
}
package knx-tp
{
class TpUartDataLinkLayer [[class_tp_uart_data_link_layer.html]]
TpUartDataLinkLayer--|>DataLinkLayer
class Bau07B0 [[class_bau07_b0.html]]
Bau07B0*--TpUartDataLinkLayer
Bau07B0--|>BauSystemBDevice
}
package knx-rf
{
class RfDataLinkLayer [[class_rf_data_link_layer.html]]
RfDataLinkLayer--|>DataLinkLayer
class Bau27B0 [[class_bau27_b0.html]]
Bau27B0*--"RfDataLinkLayer"
Bau27B0--|>BauSystemBDevice
class RfMediumObject [[class_rf_medium_object.html]]
Bau27B0*--"RfMediumObject"
class RfPhysicalLayer [[class_rf_physical_layer.html]]
"RfDataLinkLayer"*--"RfPhysicalLayer"
}
package knx-cemi-server
{
class CemiServer [[class_cemi_server.html]]
class CemiServerObject [[class_cemi_server_object.html]]
class UsbTunnelInterface [[class_usb_tunnel_inerface.html]]
CemiServer*--"UsbTunnelInterface"
Bau57B0*--"CemiServer"
Bau57B0*--"CemiServerObject"
Bau27B0*--"CemiServer"
Bau27B0*--"CemiServerObject"
Bau07B0*--"CemiServer"
Bau07B0*--"CemiServerObject"
}
CemiServer-->DataLinkLayer
DataLinkLayer-->CemiServer
}
package platform
{
class Platform [[class_platform.html]]
class ArduinoPlatform [[class_arduino_platform.html]]
class SamdPlatform [[class_samd_platform.html]]
Platform<|--ArduinoPlatform
ArduinoPlatform<|--SamdPlatform
class EspPlatform [[class_esp_platform.html]]
ArduinoPlatform<|--EspPlatform
class Esp32Platform [[class_esp32_platform.html]]
ArduinoPlatform<|--Esp32Platform
class Stm32Platform [[class_stm32_platform.html]]
ArduinoPlatform<|--Stm32Platform
class LinuxPlatform [[class_linux_platform.html]]
LinuxPlatform--|>Platform
class CC1310Platform [[class_cc1310_platform.html]]
CC1310Platform--|>Platform
class RP2040ArduinoPlatform [[class_rp2040_arduino_platform.html]]
RP2040ArduinoPlatform--|>ArduinoPlatform
}
package frontend
{
class KnxFacade [[class_knx_facade.html]]
BauSystemBDevice<--KnxFacade
}
knx-->Platform
@enduml
* \page Classdiagramm KNX coupler
* This diagramm shows the most important classes of a KNX coupler.
@startuml
skinparam monochrome true
skinparam componentStyle uml2
package "knx" {
class BusAccessUnit [[class_bus_access_unit.html]]
class DeviceObject [[class_device_object.html]]
class BauSystemBCoupler [[class_bau_system_b.html]]
BusAccessUnit<|--BauSystemB
BauSystemB<|--BauSystemBCoupler
class ApplicationProgramObject [[class_application_program_object.html]]
BauSystemB*--ApplicationProgramObject
DeviceObject--*BauSystemB
class ApplicationLayer [[class_application_layer.html]]
package knx-data-secure
{
class SecureApplicationLayer [[class_secure_application_layer.html]]
class SecurityInterfaceObject [[class_security_interface_object.html]]
SecureApplicationLayer-->SecurityInterfaceObject
SecurityInterfaceObject-->SecureApplicationLayer
BauSystemBCoupler*--"SecurityInterfaceObject"
BauSystemBCoupler*--"SecureApplicationLayer"
}
BauSystemBCoupler*--"ApplicationLayer"
ApplicationLayer<|--SecureApplicationLayer
SecureApplicationLayer--*BauSystemBCoupler
class TransportLayer [[class_transport_layer.html]]
TransportLayer--*BauSystemBCoupler
class NetworkLayerEntity [[class_network_layer_entity.html]]
class NetworkLayerCoupler [[class_network_layer.html]]
{
NetworkLayerEntity[] _networkLayerEntities
}
NetworkLayerCoupler*--"NetworkLayerEntity"
NetworkLayerCoupler--*BauSystemBCoupler
class DataLinkLayer [[class_data_link_layer.html]]
ApplicationLayer-->SecureApplicationLayer
SecureApplicationLayer-->ApplicationLayer
ApplicationLayer-->BusAccessUnit
ApplicationLayer-->TransportLayer
SecureApplicationLayer-->TransportLayer
TransportLayer-->ApplicationLayer
TransportLayer-->SecureApplicationLayer
TransportLayer-->NetworkLayerCoupler
NetworkLayerCoupler-->TransportLayer
NetworkLayerEntity-->DataLinkLayer
DataLinkLayer-->NetworkLayerEntity
SecureApplicationLayer-->DeviceObject
DataLinkLayer-->DeviceObject
package knx-ip-tp
{
class IpDataLinkLayer [[class_ip_data_link_layer.html]]
IpDataLinkLayer--|>DataLinkLayer
class TpUartDataLinkLayer [[class_tp_uart_data_link_layer.html]]
TpUartDataLinkLayer--|>DataLinkLayer
class Bau091A [[class_bau09_1a.html]]
Bau091A--|>BauSystemBCoupler
Bau091A*--IpDataLinkLayer
Bau091A*--TpUartDataLinkLayer
class RouterObject [[class_router_object.html]]
class IpParameterObject [[class_ip_parameter_object.html]]
IpParameterObject-->DeviceObject
Bau091A*--"RouterObject"
Bau091A*--IpParameterObject
IpDataLinkLayer-->IpParameterObject
}
}
package platform
{
class Platform [[class_platform.html]]
class ArduinoPlatform [[class_arduino_platform.html]]
class SamdPlatform [[class_samd_platform.html]]
Platform<|--ArduinoPlatform
ArduinoPlatform<|--SamdPlatform
class EspPlatform [[class_esp_platform.html]]
ArduinoPlatform<|--EspPlatform
class Esp32Platform [[class_esp32_platform.html]]
ArduinoPlatform<|--Esp32Platform
class Stm32Platform [[class_stm32_platform.html]]
ArduinoPlatform<|--Stm32Platform
class LinuxPlatform [[class_linux_platform.html]]
LinuxPlatform--|>Platform
class CC1310Platform [[class_cc1310_platform.html]]
CC1310Platform--|>Platform
class RP2040ArduinoPlatform [[class_rp2040_arduino_platform.html]]
RP2040ArduinoPlatform--|>ArduinoPlatform
}
package frontend
{
class KnxFacade [[class_knx_facade.html]]
BauSystemBCoupler<--KnxFacade
}
knx-->Platform
@enduml
*/
#include "knx_facade.h"

View File

@ -0,0 +1,96 @@
#include <cstring>
#include "address_table_object.h"
#include "bits.h"
#include "data_property.h"
using namespace std;
AddressTableObject::AddressTableObject(Memory& memory)
: TableObject(memory)
{
Property* properties[] =
{
new DataProperty(PID_OBJECT_TYPE, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t)OT_ADDR_TABLE)
};
TableObject::initializeProperties(sizeof(properties), properties);
}
uint16_t AddressTableObject::entryCount()
{
// after programming without GA the module hangs
if (loadState() != LS_LOADED || _groupAddresses[0] == 0xFFFF)
return 0;
return ntohs(_groupAddresses[0]);
}
uint16_t AddressTableObject::getGroupAddress(uint16_t tsap)
{
if (loadState() != LS_LOADED || tsap > entryCount() )
return 0;
return ntohs(_groupAddresses[tsap]);
}
uint16_t AddressTableObject::getTsap(uint16_t addr)
{
uint16_t size = entryCount();
#ifdef USE_BINSEARCH
uint16_t low, high, i;
low = 1;
high = size;
while (low <= high)
{
i = (low + high) / 2;
uint16_t ga = ntohs(_groupAddresses[i]);
if (ga == addr)
return i;
if (addr < ga)
high = i - 1;
else
low = i + 1 ;
}
#else
for (uint16_t i = 1; i <= size; i++)
if (ntohs(_groupAddresses[i]) == addr)
return i;
#endif
return 0;
}
#pragma region SaveRestore
const uint8_t* AddressTableObject::restore(const uint8_t* buffer)
{
buffer = TableObject::restore(buffer);
_groupAddresses = (uint16_t*)data();
return buffer;
}
#pragma endregion
bool AddressTableObject::contains(uint16_t addr)
{
return (getTsap(addr) > 0);
}
void AddressTableObject::beforeStateChange(LoadState& newState)
{
TableObject::beforeStateChange(newState);
if (newState != LS_LOADED)
return;
_groupAddresses = (uint16_t*)data();
}

View File

@ -0,0 +1,57 @@
#pragma once
#include "table_object.h"
/**
* This class represents the group address table. It provides a mapping between transport layer
* service access points (TSAP) and group addresses. The TSAP can be imagined as an index to the array
* of group addresses.
*
* See section 4.10 of @cite knx:3/5/1 for further details.
* It implements realisation type 7 (see section 4.10.7 of @cite knx:3/5/1).
*/
class AddressTableObject : public TableObject
{
public:
/**
* The constructor.
*
* @param memory This parameter is only passed to the constructor of TableObject and is not used by this class.
*/
AddressTableObject(Memory& memory);
const uint8_t* restore(const uint8_t* buffer) override;
/**
* returns the number of group addresses of the object.
*/
uint16_t entryCount();
/**
* Get the group address mapped to a TSAP.
*
* @param tsap The TSAP of which to get the group address for.
*
* @return the groupAddress if found or zero if no group address was found.
*/
uint16_t getGroupAddress(uint16_t tsap);
/**
* Get the TSAP mapped to a group address.
*
* @param groupAddress the group address of which to get the TSAP for.
*
* @return the TSAP if found or zero if no tsap was found.
*/
uint16_t getTsap(uint16_t groupAddress);
/**
* Check if the address table contains a group address.
*
* @param groupAddress the group address to check
*
* @return true if the address table contains the group address, false otherwise
*/
bool contains(uint16_t groupAddress);
protected:
void beforeStateChange(LoadState& newState) override;
private:
uint16_t* _groupAddresses = 0;
};

View File

@ -0,0 +1,606 @@
/*
This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode.
Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.
The implementation is verified against the test vectors in:
National Institute of Standards and Technology Special Publication 800-38A 2001 ED
ECB-AES128
----------
plain-text:
6bc1bee22e409f96e93d7e117393172a
ae2d8a571e03ac9c9eb76fac45af8e51
30c81c46a35ce411e5fbc1191a0a52ef
f69f2445df4f9b17ad2b417be66c3710
key:
2b7e151628aed2a6abf7158809cf4f3c
resulting cipher
3ad77bb40d7a3660a89ecaf32466ef97
f5d3d58503b9699de785895a96fdbaaf
43b1cd7f598ece23881b00e3ed030688
7b0c785e27e8ad3f8223207104725dd4
NOTE: String length must be evenly divisible by 16byte (str_len % 16 == 0)
You should pad the end of the string with zeros if this is not the case.
For AES192/256 the key size is proportionally larger.
*/
/*****************************************************************************/
/* Includes: */
/*****************************************************************************/
#include <string.h> // CBC mode, for memset
#include "aes.h"
/*****************************************************************************/
/* Defines: */
/*****************************************************************************/
// The number of columns comprising a state in AES. This is a constant in AES. Value=4
#define Nb 4
#if defined(AES256) && (AES256 == 1)
#define Nk 8
#define Nr 14
#elif defined(AES192) && (AES192 == 1)
#define Nk 6
#define Nr 12
#else
#define Nk 4 // The number of 32 bit words in a key.
#define Nr 10 // The number of rounds in AES Cipher.
#endif
// jcallan@github points out that declaring Multiply as a function
// reduces code size considerably with the Keil ARM compiler.
// See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3
#ifndef MULTIPLY_AS_A_FUNCTION
#define MULTIPLY_AS_A_FUNCTION 0
#endif
/*****************************************************************************/
/* Private variables: */
/*****************************************************************************/
// state - array holding the intermediate results during decryption.
typedef uint8_t state_t[4][4];
// The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
// The numbers below can be computed dynamically trading ROM for RAM -
// This can be useful in (embedded) bootloader applications, where ROM is often limited.
static const uint8_t sbox[256] =
{
//0 1 2 3 4 5 6 7 8 9 A B C D E F
0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
};
static const uint8_t rsbox[256] =
{
0x52, 0x09, 0x6a, 0xd5, 0x30, 0x36, 0xa5, 0x38, 0xbf, 0x40, 0xa3, 0x9e, 0x81, 0xf3, 0xd7, 0xfb,
0x7c, 0xe3, 0x39, 0x82, 0x9b, 0x2f, 0xff, 0x87, 0x34, 0x8e, 0x43, 0x44, 0xc4, 0xde, 0xe9, 0xcb,
0x54, 0x7b, 0x94, 0x32, 0xa6, 0xc2, 0x23, 0x3d, 0xee, 0x4c, 0x95, 0x0b, 0x42, 0xfa, 0xc3, 0x4e,
0x08, 0x2e, 0xa1, 0x66, 0x28, 0xd9, 0x24, 0xb2, 0x76, 0x5b, 0xa2, 0x49, 0x6d, 0x8b, 0xd1, 0x25,
0x72, 0xf8, 0xf6, 0x64, 0x86, 0x68, 0x98, 0x16, 0xd4, 0xa4, 0x5c, 0xcc, 0x5d, 0x65, 0xb6, 0x92,
0x6c, 0x70, 0x48, 0x50, 0xfd, 0xed, 0xb9, 0xda, 0x5e, 0x15, 0x46, 0x57, 0xa7, 0x8d, 0x9d, 0x84,
0x90, 0xd8, 0xab, 0x00, 0x8c, 0xbc, 0xd3, 0x0a, 0xf7, 0xe4, 0x58, 0x05, 0xb8, 0xb3, 0x45, 0x06,
0xd0, 0x2c, 0x1e, 0x8f, 0xca, 0x3f, 0x0f, 0x02, 0xc1, 0xaf, 0xbd, 0x03, 0x01, 0x13, 0x8a, 0x6b,
0x3a, 0x91, 0x11, 0x41, 0x4f, 0x67, 0xdc, 0xea, 0x97, 0xf2, 0xcf, 0xce, 0xf0, 0xb4, 0xe6, 0x73,
0x96, 0xac, 0x74, 0x22, 0xe7, 0xad, 0x35, 0x85, 0xe2, 0xf9, 0x37, 0xe8, 0x1c, 0x75, 0xdf, 0x6e,
0x47, 0xf1, 0x1a, 0x71, 0x1d, 0x29, 0xc5, 0x89, 0x6f, 0xb7, 0x62, 0x0e, 0xaa, 0x18, 0xbe, 0x1b,
0xfc, 0x56, 0x3e, 0x4b, 0xc6, 0xd2, 0x79, 0x20, 0x9a, 0xdb, 0xc0, 0xfe, 0x78, 0xcd, 0x5a, 0xf4,
0x1f, 0xdd, 0xa8, 0x33, 0x88, 0x07, 0xc7, 0x31, 0xb1, 0x12, 0x10, 0x59, 0x27, 0x80, 0xec, 0x5f,
0x60, 0x51, 0x7f, 0xa9, 0x19, 0xb5, 0x4a, 0x0d, 0x2d, 0xe5, 0x7a, 0x9f, 0x93, 0xc9, 0x9c, 0xef,
0xa0, 0xe0, 0x3b, 0x4d, 0xae, 0x2a, 0xf5, 0xb0, 0xc8, 0xeb, 0xbb, 0x3c, 0x83, 0x53, 0x99, 0x61,
0x17, 0x2b, 0x04, 0x7e, 0xba, 0x77, 0xd6, 0x26, 0xe1, 0x69, 0x14, 0x63, 0x55, 0x21, 0x0c, 0x7d
};
// The round constant word array, Rcon[i], contains the values given by
// x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
static const uint8_t Rcon[11] =
{
0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36
};
/*
* Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12),
* that you can remove most of the elements in the Rcon array, because they are unused.
*
* From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
*
* "Only the first some of these constants are actually used up to rcon[10] for AES-128 (as 11 round keys are needed),
* up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
*/
/*****************************************************************************/
/* Private functions: */
/*****************************************************************************/
/*
static uint8_t getSBoxValue(uint8_t num)
{
return sbox[num];
}
*/
#define getSBoxValue(num) (sbox[(num)])
/*
static uint8_t getSBoxInvert(uint8_t num)
{
return rsbox[num];
}
*/
#define getSBoxInvert(num) (rsbox[(num)])
// This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
static void KeyExpansion(uint8_t* RoundKey, const uint8_t* Key)
{
unsigned i, j, k;
uint8_t tempa[4]; // Used for the column/row operations
// The first round key is the key itself.
for (i = 0; i < Nk; ++i)
{
RoundKey[(i * 4) + 0] = Key[(i * 4) + 0];
RoundKey[(i * 4) + 1] = Key[(i * 4) + 1];
RoundKey[(i * 4) + 2] = Key[(i * 4) + 2];
RoundKey[(i * 4) + 3] = Key[(i * 4) + 3];
}
// All other round keys are found from the previous round keys.
for (i = Nk; i < Nb * (Nr + 1); ++i)
{
{
k = (i - 1) * 4;
tempa[0] = RoundKey[k + 0];
tempa[1] = RoundKey[k + 1];
tempa[2] = RoundKey[k + 2];
tempa[3] = RoundKey[k + 3];
}
if (i % Nk == 0)
{
// This function shifts the 4 bytes in a word to the left once.
// [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
// Function RotWord()
{
const uint8_t u8tmp = tempa[0];
tempa[0] = tempa[1];
tempa[1] = tempa[2];
tempa[2] = tempa[3];
tempa[3] = u8tmp;
}
// SubWord() is a function that takes a four-byte input word and
// applies the S-box to each of the four bytes to produce an output word.
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
tempa[0] = tempa[0] ^ Rcon[i / Nk];
}
#if defined(AES256) && (AES256 == 1)
if (i % Nk == 4)
{
// Function Subword()
{
tempa[0] = getSBoxValue(tempa[0]);
tempa[1] = getSBoxValue(tempa[1]);
tempa[2] = getSBoxValue(tempa[2]);
tempa[3] = getSBoxValue(tempa[3]);
}
}
#endif
j = i * 4;
k = (i - Nk) * 4;
RoundKey[j + 0] = RoundKey[k + 0] ^ tempa[0];
RoundKey[j + 1] = RoundKey[k + 1] ^ tempa[1];
RoundKey[j + 2] = RoundKey[k + 2] ^ tempa[2];
RoundKey[j + 3] = RoundKey[k + 3] ^ tempa[3];
}
}
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key)
{
KeyExpansion(ctx->RoundKey, key);
}
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv)
{
KeyExpansion(ctx->RoundKey, key);
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv)
{
memcpy (ctx->Iv, iv, AES_BLOCKLEN);
}
#endif
// This function adds the round key to state.
// The round key is added to the state by an XOR function.
static void AddRoundKey(uint8_t round, state_t* state, const uint8_t* RoundKey)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[i][j] ^= RoundKey[(round * Nb * 4) + (i * Nb) + j];
}
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void SubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxValue((*state)[j][i]);
}
}
}
// The ShiftRows() function shifts the rows in the state to the left.
// Each row is shifted with different offset.
// Offset = Row number. So the first row is not shifted.
static void ShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to left
temp = (*state)[0][1];
(*state)[0][1] = (*state)[1][1];
(*state)[1][1] = (*state)[2][1];
(*state)[2][1] = (*state)[3][1];
(*state)[3][1] = temp;
// Rotate second row 2 columns to left
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to left
temp = (*state)[0][3];
(*state)[0][3] = (*state)[3][3];
(*state)[3][3] = (*state)[2][3];
(*state)[2][3] = (*state)[1][3];
(*state)[1][3] = temp;
}
static uint8_t xtime(uint8_t x)
{
return ((x << 1) ^ (((x >> 7) & 1) * 0x1b));
}
// MixColumns function mixes the columns of the state matrix
static void MixColumns(state_t* state)
{
uint8_t i;
uint8_t Tmp, Tm, t;
for (i = 0; i < 4; ++i)
{
t = (*state)[i][0];
Tmp = (*state)[i][0] ^ (*state)[i][1] ^ (*state)[i][2] ^ (*state)[i][3] ;
Tm = (*state)[i][0] ^ (*state)[i][1] ;
Tm = xtime(Tm);
(*state)[i][0] ^= Tm ^ Tmp ;
Tm = (*state)[i][1] ^ (*state)[i][2] ;
Tm = xtime(Tm);
(*state)[i][1] ^= Tm ^ Tmp ;
Tm = (*state)[i][2] ^ (*state)[i][3] ;
Tm = xtime(Tm);
(*state)[i][2] ^= Tm ^ Tmp ;
Tm = (*state)[i][3] ^ t ;
Tm = xtime(Tm);
(*state)[i][3] ^= Tm ^ Tmp ;
}
}
// Multiply is used to multiply numbers in the field GF(2^8)
// Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
// The compiler seems to be able to vectorize the operation better this way.
// See https://github.com/kokke/tiny-AES-c/pull/34
#if MULTIPLY_AS_A_FUNCTION
static uint8_t Multiply(uint8_t x, uint8_t y)
{
return (((y & 1) * x) ^
((y >> 1 & 1) * xtime(x)) ^
((y >> 2 & 1) * xtime(xtime(x))) ^
((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^
((y >> 4 & 1) * xtime(xtime(xtime(xtime(x)))))); /* this last call to xtime() can be omitted */
}
#else
#define Multiply(x, y) \
( ((y & 1) * x) ^ \
((y>>1 & 1) * xtime(x)) ^ \
((y>>2 & 1) * xtime(xtime(x))) ^ \
((y>>3 & 1) * xtime(xtime(xtime(x)))) ^ \
((y>>4 & 1) * xtime(xtime(xtime(xtime(x)))))) \
#endif
#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
// MixColumns function mixes the columns of the state matrix.
// The method used to multiply may be difficult to understand for the inexperienced.
// Please use the references to gain more information.
static void InvMixColumns(state_t* state)
{
int i;
uint8_t a, b, c, d;
for (i = 0; i < 4; ++i)
{
a = (*state)[i][0];
b = (*state)[i][1];
c = (*state)[i][2];
d = (*state)[i][3];
(*state)[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
(*state)[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
(*state)[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
(*state)[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
}
}
// The SubBytes Function Substitutes the values in the
// state matrix with values in an S-box.
static void InvSubBytes(state_t* state)
{
uint8_t i, j;
for (i = 0; i < 4; ++i)
{
for (j = 0; j < 4; ++j)
{
(*state)[j][i] = getSBoxInvert((*state)[j][i]);
}
}
}
static void InvShiftRows(state_t* state)
{
uint8_t temp;
// Rotate first row 1 columns to right
temp = (*state)[3][1];
(*state)[3][1] = (*state)[2][1];
(*state)[2][1] = (*state)[1][1];
(*state)[1][1] = (*state)[0][1];
(*state)[0][1] = temp;
// Rotate second row 2 columns to right
temp = (*state)[0][2];
(*state)[0][2] = (*state)[2][2];
(*state)[2][2] = temp;
temp = (*state)[1][2];
(*state)[1][2] = (*state)[3][2];
(*state)[3][2] = temp;
// Rotate third row 3 columns to right
temp = (*state)[0][3];
(*state)[0][3] = (*state)[1][3];
(*state)[1][3] = (*state)[2][3];
(*state)[2][3] = (*state)[3][3];
(*state)[3][3] = temp;
}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
// Cipher is the main function that encrypts the PlainText.
static void Cipher(state_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(0, state, RoundKey);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr rounds are executed in the loop below.
// Last one without MixColumns()
for (round = 1; ; ++round)
{
SubBytes(state);
ShiftRows(state);
if (round == Nr)
{
break;
}
MixColumns(state);
AddRoundKey(round, state, RoundKey);
}
// Add round key to last round
AddRoundKey(Nr, state, RoundKey);
}
#if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
static void InvCipher(state_t* state, const uint8_t* RoundKey)
{
uint8_t round = 0;
// Add the First round key to the state before starting the rounds.
AddRoundKey(Nr, state, RoundKey);
// There will be Nr rounds.
// The first Nr-1 rounds are identical.
// These Nr rounds are executed in the loop below.
// Last one without InvMixColumn()
for (round = (Nr - 1); ; --round)
{
InvShiftRows(state);
InvSubBytes(state);
AddRoundKey(round, state, RoundKey);
if (round == 0)
{
break;
}
InvMixColumns(state);
}
}
#endif // #if (defined(CBC) && CBC == 1) || (defined(ECB) && ECB == 1)
/*****************************************************************************/
/* Public functions: */
/*****************************************************************************/
#if defined(ECB) && (ECB == 1)
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call encrypts the PlainText with the Key using AES algorithm.
Cipher((state_t*)buf, ctx->RoundKey);
}
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf)
{
// The next function call decrypts the PlainText with the Key using AES algorithm.
InvCipher((state_t*)buf, ctx->RoundKey);
}
#endif // #if defined(ECB) && (ECB == 1)
#if defined(CBC) && (CBC == 1)
static void XorWithIv(uint8_t* buf, const uint8_t* Iv)
{
uint8_t i;
for (i = 0; i < AES_BLOCKLEN; ++i) // The block in AES is always 128bit no matter the key size
{
buf[i] ^= Iv[i];
}
}
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
uintptr_t i;
uint8_t* Iv = ctx->Iv;
for (i = 0; i < length; i += AES_BLOCKLEN)
{
XorWithIv(buf, Iv);
Cipher((state_t*)buf, ctx->RoundKey);
Iv = buf;
buf += AES_BLOCKLEN;
}
/* store Iv in ctx for next call */
memcpy(ctx->Iv, Iv, AES_BLOCKLEN);
}
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
uintptr_t i;
uint8_t storeNextIv[AES_BLOCKLEN];
for (i = 0; i < length; i += AES_BLOCKLEN)
{
memcpy(storeNextIv, buf, AES_BLOCKLEN);
InvCipher((state_t*)buf, ctx->RoundKey);
XorWithIv(buf, ctx->Iv);
memcpy(ctx->Iv, storeNextIv, AES_BLOCKLEN);
buf += AES_BLOCKLEN;
}
}
#endif // #if defined(CBC) && (CBC == 1)
#if defined(CTR) && (CTR == 1)
/* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length)
{
uint8_t buffer[AES_BLOCKLEN];
unsigned i;
int bi;
for (i = 0, bi = AES_BLOCKLEN; i < length; ++i, ++bi)
{
if (bi == AES_BLOCKLEN) /* we need to regen xor compliment in buffer */
{
memcpy(buffer, ctx->Iv, AES_BLOCKLEN);
Cipher((state_t*)buffer, ctx->RoundKey);
/* Increment Iv and handle overflow */
for (bi = (AES_BLOCKLEN - 1); bi >= 0; --bi)
{
/* inc will overflow */
if (ctx->Iv[bi] == 255)
{
ctx->Iv[bi] = 0;
continue;
}
ctx->Iv[bi] += 1;
break;
}
bi = 0;
}
buf[i] = (buf[i] ^ buffer[bi]);
}
}
#endif // #if defined(CTR) && (CTR == 1)

View File

@ -0,0 +1,90 @@
#ifndef _AES_H_
#define _AES_H_
#include <stdint.h>
// #define the macros below to 1/0 to enable/disable the mode of operation.
//
// CBC enables AES encryption in CBC-mode of operation.
// CTR enables encryption in counter-mode.
// ECB enables the basic ECB 16-byte block algorithm. All can be enabled simultaneously.
// The #ifndef-guard allows it to be configured before #include'ing or at compile time.
#ifndef CBC
#define CBC 1
#endif
#ifndef ECB
#define ECB 1
#endif
#ifndef CTR
#define CTR 1
#endif
#define AES128 1
//#define AES192 1
//#define AES256 1
#define AES_BLOCKLEN 16 // Block length in bytes - AES is 128b block only
#if defined(AES256) && (AES256 == 1)
#define AES_KEYLEN 32
#define AES_keyExpSize 240
#elif defined(AES192) && (AES192 == 1)
#define AES_KEYLEN 24
#define AES_keyExpSize 208
#else
#define AES_KEYLEN 16 // Key length in bytes
#define AES_keyExpSize 176
#endif
struct AES_ctx
{
uint8_t RoundKey[AES_keyExpSize];
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
uint8_t Iv[AES_BLOCKLEN];
#endif
};
void AES_init_ctx(struct AES_ctx* ctx, const uint8_t* key);
#if (defined(CBC) && (CBC == 1)) || (defined(CTR) && (CTR == 1))
void AES_init_ctx_iv(struct AES_ctx* ctx, const uint8_t* key, const uint8_t* iv);
void AES_ctx_set_iv(struct AES_ctx* ctx, const uint8_t* iv);
#endif
#if defined(ECB) && (ECB == 1)
// buffer size is exactly AES_BLOCKLEN bytes;
// you need only AES_init_ctx as IV is not used in ECB
// NB: ECB is considered insecure for most uses
void AES_ECB_encrypt(const struct AES_ctx* ctx, uint8_t* buf);
void AES_ECB_decrypt(const struct AES_ctx* ctx, uint8_t* buf);
#endif // #if defined(ECB) && (ECB == !)
#if defined(CBC) && (CBC == 1)
// buffer size MUST be mutile of AES_BLOCKLEN;
// Suggest https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx via AES_init_ctx_iv() or AES_ctx_set_iv()
// no IV should ever be reused with the same key
void AES_CBC_encrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
void AES_CBC_decrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
#endif // #if defined(CBC) && (CBC == 1)
#if defined(CTR) && (CTR == 1)
// Same function for encrypting as for decrypting.
// IV is incremented for every block, and used after encryption as XOR-compliment for output
// Suggesting https://en.wikipedia.org/wiki/Padding_(cryptography)#PKCS7 for padding scheme
// NOTES: you need to set IV in ctx with AES_init_ctx_iv() or AES_ctx_set_iv()
// no IV should ever be reused with the same key
void AES_CTR_xcrypt_buffer(struct AES_ctx* ctx, uint8_t* buf, uint32_t length);
#endif // #if defined(CTR) && (CTR == 1)
#endif // _AES_H_

View File

@ -0,0 +1,12 @@
#ifndef _AES_HPP_
#define _AES_HPP_
#ifndef __cplusplus
#error Do not include the hpp header in a c project!
#endif //__cplusplus
extern "C" {
#include "aes.h"
}
#endif //_AES_HPP_

View File

@ -0,0 +1,59 @@
#include "apdu.h"
#include "cemi_frame.h"
#include "bits.h"
APDU::APDU(uint8_t* data, CemiFrame& frame): _data(data), _frame(frame)
{
}
ApduType APDU::type()
{
uint16_t apci;
apci = getWord(_data);
popWord(apci, _data);
apci &= 0x3ff;
if ((apci >> 6) < 11 && (apci >> 6) != 7) //short apci
apci &= 0x3c0;
return (ApduType)apci;
}
void APDU::type(ApduType atype)
{
// ApduType is in big endian so convert to host first, pushWord converts back
pushWord((uint16_t)atype, _data);
}
uint8_t* APDU::data()
{
return _data + 1;
}
CemiFrame& APDU::frame()
{
return _frame;
}
uint8_t APDU::length() const
{
return _frame.npdu().octetCount();
}
void APDU::printPDU()
{
print("APDU: ");
print(type(), HEX);
print(" ");
print(_data[0] & 0x3, HEX);
for (uint8_t i = 1; i < length() + 1; ++i)
{
if (i)
print(" ");
print(_data[i], HEX);
}
println();
}

View File

@ -0,0 +1,53 @@
#pragma once
#include <stdint.h>
#include "knx_types.h"
class CemiFrame;
/**
* This class represents an Application Protocol Data Unit. It is part of a CemiFrame.
*/
class APDU
{
friend class CemiFrame;
public:
/**
* Get the type of the APDU.
*/
ApduType type();
/**
* Set the type of the APDU.
*/
void type(ApduType atype);
/**
* Get a pointer to the data.
*/
uint8_t* data();
/**
* Get the CemiFrame this APDU is part of.
*/
CemiFrame& frame();
/**
* Get the length of the APDU. (This is actually the octet count of the NPDU.)
*/
uint8_t length() const;
/**
* Print the contents of the APDU to console.
*/
void printPDU();
protected:
/**
* The constructor.
* @param data The data of the APDU. Encoding depends on the ::ApduType. The class doesn't
* take possession of this pointer.
* @param frame The CemiFrame this APDU is part of.
*/
APDU(uint8_t* data, CemiFrame& frame);
private:
uint8_t* _data = 0;
CemiFrame& _frame;
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,228 @@
#pragma once
#include <stdint.h>
#include "knx_types.h"
#include "apdu.h"
class AssociationTableObject;
class BusAccessUnit;
class TransportLayer;
/**
* This is an implementation of the application layer as specified in @cite knx:3/5/1.
* It provides methods for the BusAccessUnit to do different things and translates this
* call to an APDU and calls the correct method of the TransportLayer.
* It also takes calls from TransportLayer, decodes the submitted APDU and calls the corresponding
* methods of the BusAccessUnit class.
*/
class ApplicationLayer
{
public:
/**
* The constructor.
* @param assocTable The AssociationTable is used to translate between asap (i.e. group objects) and group addresses.
* @param bau methods are called here depending of the content of the APDU
*/
ApplicationLayer(BusAccessUnit& bau);
/**
* Assigns the TransportLayer to which encoded APDU are submitted to.
*/
void transportLayer(TransportLayer& layer);
void associationTableObject(AssociationTableObject& assocTable);
// from transport layer
// Note: without data secure feature, the application layer is just used with SecurityControl.dataSecurity = None
// hooks that can be implemented by derived class (e.g. SecureApplicationLayer)
#pragma region Transport - Layer - Callbacks
/**
* Somebody send us an APDU via multicast communication. See 3.2 of @cite knx:3/3/4.
* See also ApplicationLayer::dataGroupConfirm and TransportLayer::dataGroupRequest.
* This method is called by the TransportLayer.
*
* @param tsap used the find the correct GroupObject with the help of the AssociationTableObject.
* See 3.1.1 of @cite knx:3/3/7
*
* @param apdu The submitted APDU.
*
* @param priority The ::Priority of the received request.
*
* @param hopType Should routing be endless or should the NetworkLayer::hopCount be used? See also ::HopCountType.
*/
virtual void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu);
/**
* Report the status of an APDU that we sent via multicast communication back to us. See 3.2 of @cite knx:3/3/4.
* See also ApplicationLayer::dataGroupConfirm and TransportLayer::dataGroupRequest. This method is called by
* the TransportLayer.
*
* @param tsap used the find the correct GroupObject with the help of the AssociationTableObject.
* See 3.1.1 of @cite knx:3/3/7
*
* @param apdu The submitted APDU.
*
* @param priority The ::Priority of the received request.
*
* @param hopType Should routing be endless or should the NetworkLayer::hopCount be used? See also ::HopCountType.
*
* @param status Was the request successful?
*
* @param ack Did we want a DataLinkLayer acknowledgement? See ::AckType.
*/
virtual void dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status);
virtual void dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu);
virtual void dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, bool status);
virtual void dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu);
virtual void dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status);
virtual void dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu);
virtual void dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status);
virtual void dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu);
virtual void dataConnectedConfirm(uint16_t tsap);
void connectIndication(uint16_t tsap);
void connectConfirm(uint16_t destination, uint16_t tsap, bool status);
void disconnectIndication(uint16_t tsap);
void disconnectConfirm(Priority priority, uint16_t tsap, bool status);
#pragma endregion
#pragma region from bau
void groupValueReadRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl);
void groupValueReadResponse(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* data, uint8_t dataLength);
void groupValueWriteRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* data, uint8_t dataLength);
void individualAddressWriteRequest(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, uint16_t newaddress);
void individualAddressReadRequest(AckType ack, HopCountType hopType, const SecurityControl& secCtrl);
void individualAddressReadResponse(AckType ack, HopCountType hopType, const SecurityControl& secCtrl);
void individualAddressSerialNumberReadRequest(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* serialNumber);
void individualAddressSerialNumberReadResponse(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* serialNumber,
uint16_t domainAddress);
void individualAddressSerialNumberWriteRequest(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* serialNumber,
uint16_t newaddress);
void deviceDescriptorReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint8_t descriptorType);
void deviceDescriptorReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint8_t descriptorType, uint8_t* deviceDescriptor);
void connectRequest(uint16_t destination, Priority priority);
void disconnectRequest(Priority priority);
bool isConnected();
void restartRequest(AckType ack, Priority priority, HopCountType hopType, const SecurityControl& secCtrl);
void restartResponse(AckType ack, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t errorCode, uint16_t processTime);
void propertyValueReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex);
void propertyValueReadResponse(AckType ack, 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);
void propertyValueExtReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint16_t objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length);
void propertyValueExtWriteConResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint16_t objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t returnCode);
void propertyValueWriteRequest(AckType ack, 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);
void adcReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint8_t channelNr, uint8_t readCount, int16_t value);
void functionPropertyStateResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint8_t objectIndex, uint8_t propertyId, uint8_t* resultData, uint8_t resultLength);
void functionPropertyExtStateResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint16_t objectType, uint8_t objectInstance, uint16_t propertyId, uint8_t* resultData, uint8_t resultLength);
void propertyDescriptionReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex);
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,
uint32_t memoryAddress, uint8_t* memoryData);
void memoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t* data);
void userMemoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint32_t memoryAddress);
void userMemoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint32_t memoryAddress, uint8_t* memoryData);
void userMemoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint32_t memoryAddress, uint8_t* memoryData);
void userManufacturerInfoReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl);
void userManufacturerInfoReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint8_t* info);
void authorizeRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint32_t key);
void authorizeResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level);
void keyWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level, uint32_t key);
void keyWriteResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level);
void systemNetworkParameterReadResponse(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint16_t objectType,
uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength,
uint8_t* testResult, uint16_t testResultLength);
void domainAddressSerialNumberReadResponse(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* rfDoA,
const uint8_t* knxSerialNumber);
void IndividualAddressSerialNumberReadResponse(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* domainAddress,
const uint8_t* knxSerialNumber);
#pragma endregion
protected:
#pragma region hooks
void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl);
void dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap,
APDU& apdu, const SecurityControl& secCtrl, bool status);
void dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu, const SecurityControl& secCtrl);
void dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl, bool status);
void dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu, const SecurityControl& secCtrl);
void dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl, bool status);
void dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu, const SecurityControl& secCtrl);
void dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl, bool status);
void dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl);
void dataConnectedConfirm(uint16_t tsap, const SecurityControl& secCtrl);
#pragma endregion
// to transport layer
virtual void dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl);
virtual void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl);
virtual void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, const SecurityControl& secCtrl);
virtual void dataIndividualRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t destination, APDU& apdu, const SecurityControl& secCtrl);
virtual void dataConnectedRequest(uint16_t tsap, Priority priority, APDU& apdu, const SecurityControl& secCtrl); // apdu must be valid until it was confirmed
uint16_t getConnectedTsasp()
{
return _connectedTsap;
}
// Protected: we need to access it in derived class SecureApplicationLayer
TransportLayer* _transportLayer = 0;
static const SecurityControl noSecurity;
private:
void propertyDataSend(ApduType type, AckType ack, 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);
void propertyExtDataSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
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);
void individualIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl);
void individualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, const SecurityControl& secCtrl, bool status);
void individualSend(AckType ack, HopCountType hopType, Priority priority, uint16_t asap, APDU& apdu, const SecurityControl& secCtrl);
uint16_t _savedAsapReadRequest = 0;
uint16_t _savedAsapWriteRequest = 0;
uint16_t _savedAsapResponse = 0;
AssociationTableObject* _assocTable = nullptr;
BusAccessUnit& _bau;
int32_t _connectedTsap = -1;
};

View File

@ -0,0 +1,99 @@
#include "application_program_object.h"
#include "bits.h"
#include "data_property.h"
#include "callback_property.h"
#include "dptconvert.h"
#include <cstring>
ApplicationProgramObject::ApplicationProgramObject(Memory& memory)
#if MASK_VERSION == 0x091A
: TableObject(memory, 0x0100, 0x0100)
#else
: TableObject(memory)
#endif
{
Property* properties[] =
{
new DataProperty(PID_OBJECT_TYPE, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t)OT_APPLICATION_PROG),
new DataProperty(PID_PROG_VERSION, true, PDT_GENERIC_05, 1, ReadLv3 | WriteLv3),
new CallbackProperty<ApplicationProgramObject>(this, PID_PEI_TYPE, false, PDT_UNSIGNED_CHAR, 1, ReadLv3 | WriteLv0,
[](ApplicationProgramObject * io, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t {
if (start == 0)
{
uint16_t currentNoOfElements = 1;
pushWord(currentNoOfElements, data);
return 1;
}
data[0] = 0;
return 1;
})
};
TableObject::initializeProperties(sizeof(properties), properties);
}
uint8_t* ApplicationProgramObject::save(uint8_t* buffer)
{
uint8_t programVersion[5];
property(PID_PROG_VERSION)->read(programVersion);
buffer = pushByteArray(programVersion, 5, buffer);
return TableObject::save(buffer);
}
const uint8_t* ApplicationProgramObject::restore(const uint8_t* buffer)
{
uint8_t programVersion[5];
buffer = popByteArray(programVersion, 5, buffer);
property(PID_PROG_VERSION)->write(programVersion);
return TableObject::restore(buffer);
}
uint16_t ApplicationProgramObject::saveSize()
{
return TableObject::saveSize() + 5; // sizeof(programVersion)
}
uint8_t* ApplicationProgramObject::data(uint32_t addr)
{
return TableObject::data() + addr;
}
uint8_t ApplicationProgramObject::getByte(uint32_t addr)
{
return *(TableObject::data() + addr);
}
uint16_t ApplicationProgramObject::getWord(uint32_t addr)
{
return ::getWord(TableObject::data() + addr);
}
uint32_t ApplicationProgramObject::getInt(uint32_t addr)
{
return ::getInt(TableObject::data() + addr);
}
double ApplicationProgramObject::getFloat(uint32_t addr, ParameterFloatEncodings encoding)
{
switch (encoding)
{
case Float_Enc_DPT9:
return float16FromPayload(TableObject::data() + addr, 0);
break;
case Float_Enc_IEEE754Single:
return float32FromPayload(TableObject::data() + addr, 0);
break;
case Float_Enc_IEEE754Double:
return float64FromPayload(TableObject::data() + addr, 0);
break;
default:
return 0;
break;
}
}

View File

@ -0,0 +1,18 @@
#pragma once
#include "table_object.h"
#include "bits.h"
class ApplicationProgramObject : public TableObject
{
public:
ApplicationProgramObject(Memory& memory);
uint8_t* save(uint8_t* buffer) override;
const uint8_t* restore(const uint8_t* buffer) override;
uint16_t saveSize() override;
uint8_t* data(uint32_t addr);
uint8_t getByte(uint32_t addr);
uint16_t getWord(uint32_t addr);
uint32_t getInt(uint32_t addr);
double getFloat(uint32_t addr, ParameterFloatEncodings encoding);
};

View File

@ -0,0 +1,174 @@
#include <cstring>
#include "association_table_object.h"
#include "bits.h"
#include "data_property.h"
using namespace std;
AssociationTableObject::AssociationTableObject(Memory& memory)
: TableObject(memory)
{
Property* properties[] =
{
new DataProperty(PID_OBJECT_TYPE, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t)OT_ASSOC_TABLE),
new DataProperty(PID_TABLE, false, PDT_GENERIC_04, 65535, ReadLv3 | WriteLv0) //FIXME: implement correctly
};
TableObject::initializeProperties(sizeof(properties), properties);
}
uint16_t AssociationTableObject::entryCount()
{
return ntohs(_tableData[0]);
}
uint16_t AssociationTableObject::getTSAP(uint16_t idx)
{
if (idx >= entryCount())
return 0;
return ntohs(_tableData[2 * idx + 1]);
}
uint16_t AssociationTableObject::getASAP(uint16_t idx)
{
if (idx >= entryCount())
return 0;
return ntohs(_tableData[2 * idx + 2]);
}
// after any table change the table is checked if it allows
// binary search access. If not, sortedEntryCount stays 0,
// otherwise sortedEntryCount represents size of bin search array
void AssociationTableObject::prepareBinarySearch()
{
sortedEntryCount = 0;
#ifdef USE_BINSEARCH
uint16_t lastASAP = 0;
uint16_t currentASAP = 0;
uint16_t lookupIdx = 0;
uint16_t lookupASAP = 0;
// we iterate through all ASAP
// the first n ASAP are sorted (strictly increasing number), these are assigning sending TSAP
// the remaining ASAP have to be all repetitions, otherwise we set sortedEntryCount to 0, which forces linear search
if (_tableData != nullptr)
{
for (uint16_t idx = 0; idx < entryCount(); idx++)
{
currentASAP = getASAP(idx);
if (sortedEntryCount)
{
// look if the remaining ASAP exist in the previously sorted list.
while (lookupIdx < sortedEntryCount)
{
lookupASAP = getASAP(lookupIdx);
if (currentASAP <= lookupASAP)
break; // while
else
lookupIdx++;
}
if (currentASAP < lookupASAP || lookupIdx >= sortedEntryCount)
{
// a new ASAP found, we force linear search
sortedEntryCount = 0;
break; // for
}
}
else
{
// check for strictly increasing ASAP
if (currentASAP > lastASAP)
lastASAP = currentASAP;
else
{
sortedEntryCount = idx; // last found index indicates end of sorted list
idx--; // current item has to be handled as remaining ASAP
}
}
}
// in case complete table is strictly increasing
if (lookupIdx == 0 && sortedEntryCount == 0)
sortedEntryCount = entryCount();
}
#endif
}
const uint8_t* AssociationTableObject::restore(const uint8_t* buffer)
{
buffer = TableObject::restore(buffer);
_tableData = (uint16_t*)data();
prepareBinarySearch();
return buffer;
}
// return type is int32 so that we can return uint16 and -1
int32_t AssociationTableObject::translateAsap(uint16_t asap)
{
// sortedEntryCount is determined in prepareBinarySearch()
// if ETS provides strictly increasing numbers for ASAP
// represents the size of the array to search
if (sortedEntryCount)
{
uint16_t low = 0;
uint16_t high = sortedEntryCount - 1;
while (low <= high)
{
uint16_t i = (low + high) / 2;
uint16_t asap_i = getASAP(i);
if (asap_i == asap)
return getTSAP(i);
if (asap_i > asap)
high = i - 1;
else
low = i + 1 ;
}
}
else
{
// if ASAP numbers are not strictly increasing linear seach is used
for (uint16_t i = 0; i < entryCount(); i++)
if (getASAP(i) == asap)
return getTSAP(i);
}
return -1;
}
void AssociationTableObject::beforeStateChange(LoadState& newState)
{
TableObject::beforeStateChange(newState);
if (newState != LS_LOADED)
return;
_tableData = (uint16_t*)data();
prepareBinarySearch();
}
int32_t AssociationTableObject::nextAsap(uint16_t tsap, uint16_t& startIdx)
{
uint16_t entries = entryCount();
for (uint16_t i = startIdx; i < entries; i++)
{
startIdx = i + 1;
if (getTSAP(i) == tsap)
{
return getASAP(i);
}
}
return -1;
}

View File

@ -0,0 +1,25 @@
#pragma once
#include "table_object.h"
class AssociationTableObject : public TableObject
{
public:
AssociationTableObject(Memory& memory);
const uint8_t* restore(const uint8_t* buffer) override;
int32_t translateAsap(uint16_t asap);
int32_t nextAsap(uint16_t tsap, uint16_t& startIdx);
protected:
void beforeStateChange(LoadState& newState) override;
private:
uint16_t entryCount();
uint16_t getTSAP(uint16_t idx);
uint16_t getASAP(uint16_t idx);
void prepareBinarySearch();
uint16_t* _tableData = 0;
uint16_t sortedEntryCount;
};

View File

@ -0,0 +1,392 @@
#include "bau.h"
void BusAccessUnit::groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, bool status)
{
}
void BusAccessUnit::groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl)
{
}
void BusAccessUnit::groupValueReadResponseConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopTtype, const SecurityControl& secCtrl, uint8_t* data, uint8_t dataLength, bool status)
{
}
void BusAccessUnit::groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* data, uint8_t dataLength)
{
}
void BusAccessUnit::groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* data, uint8_t dataLength, bool status)
{
}
void BusAccessUnit::groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* data, uint8_t dataLength)
{
}
void BusAccessUnit::individualAddressWriteLocalConfirm(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, uint16_t newaddress, bool status)
{
}
void BusAccessUnit::individualAddressWriteIndication(HopCountType hopType, const SecurityControl& secCtrl, uint16_t newaddress)
{
}
void BusAccessUnit::individualAddressReadLocalConfirm(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, bool status)
{
}
void BusAccessUnit::individualAddressReadIndication(HopCountType hopType, const SecurityControl& secCtrl)
{
}
void BusAccessUnit::individualAddressReadResponseConfirm(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, bool status)
{
}
void BusAccessUnit::individualAddressReadAppLayerConfirm(HopCountType hopType, const SecurityControl& secCtrl, uint16_t individualAddress)
{
}
void BusAccessUnit::individualAddressSerialNumberReadLocalConfirm(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* serialNumber, bool status)
{
}
void BusAccessUnit::individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* knxSerialNumber)
{
}
void BusAccessUnit::individualAddressSerialNumberReadResponseConfirm(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* serialNumber, uint16_t domainAddress, bool status)
{
}
void BusAccessUnit::individualAddressSerialNumberReadAppLayerConfirm(HopCountType hopType, const SecurityControl& secCtrl, uint8_t* serialNumber, uint16_t individualAddress, uint16_t domainAddress)
{
}
void BusAccessUnit::individualAddressSerialNumberWriteLocalConfirm(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* serialNumber, uint16_t newaddress, bool status)
{
}
void BusAccessUnit::individualAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint16_t newIndividualAddress,
uint8_t* knxSerialNumber)
{
}
void BusAccessUnit::deviceDescriptorReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t descriptorType, bool status)
{
}
void BusAccessUnit::deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t descriptorType)
{
}
void BusAccessUnit::deviceDescriptorReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t descriptor_type,
uint8_t* device_descriptor, bool status)
{
}
void BusAccessUnit::deviceDescriptorReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t descriptortype, uint8_t* deviceDescriptor)
{
}
void BusAccessUnit::restartRequestLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, bool status)
{
}
void BusAccessUnit::restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, RestartType restartType, EraseCode eraseCode, uint8_t channel)
{
}
void BusAccessUnit::propertyValueReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, bool status)
{
}
void BusAccessUnit::propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex)
{
}
void BusAccessUnit::propertyValueExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ObjectType objectType, uint8_t objectInstance,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex)
{
}
void BusAccessUnit::functionPropertyCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t* data, uint8_t length)
{
}
void BusAccessUnit::functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t* data, uint8_t length)
{
}
void BusAccessUnit::functionPropertyExtCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ObjectType objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t* data, uint8_t length)
{
}
void BusAccessUnit::functionPropertyExtStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ObjectType objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t* data, uint8_t length)
{
}
void BusAccessUnit::propertyValueReadResponseConfirm(AckType ack, 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, bool status)
{
}
void BusAccessUnit::propertyValueReadAppLayerConfirm(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)
{
}
void BusAccessUnit::propertyValueWriteLocalConfirm(AckType ack, 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, bool status)
{
}
void BusAccessUnit::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)
{
}
void BusAccessUnit::propertyValueExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ObjectType objectType, uint8_t objectInstance, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool confirmed)
{
}
void BusAccessUnit::propertyDescriptionReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool status)
{
}
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)
{
}
void BusAccessUnit::propertyDescriptionReadResponseConfirm(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, bool status)
{
}
void BusAccessUnit::propertyDescriptionReadAppLayerConfirm(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 BusAccessUnit::memoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint16_t memoryAddress, bool status)
{
}
void BusAccessUnit::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint16_t memoryAddress)
{
}
void BusAccessUnit::memoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data, bool status)
{
}
void BusAccessUnit::memoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data)
{
}
void BusAccessUnit::memoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data, bool status)
{
}
void BusAccessUnit::memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint16_t memoryAddress, uint8_t* data)
{
}
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)
{
}
void BusAccessUnit::memoryExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress)
{
}
void BusAccessUnit::memoryExtReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* data, bool status)
{
}
void BusAccessUnit::memoryExtReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* data)
{
}
void BusAccessUnit::memoryExtWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* data, bool status)
{
}
void BusAccessUnit::memoryExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* data)
{
}
void BusAccessUnit::memoryExtWriteResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* data, bool status)
{
}
void BusAccessUnit::memoryExtWriteAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* data)
{
}
void BusAccessUnit::userMemoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, bool status)
{
}
void BusAccessUnit::userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress)
{
}
void BusAccessUnit::userMemoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData, bool status)
{
}
void BusAccessUnit::userMemoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData)
{
}
void BusAccessUnit::userMemoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData, bool status)
{
}
void BusAccessUnit::userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* memoryData)
{
}
void BusAccessUnit::userManufacturerInfoLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, bool status)
{
}
void BusAccessUnit::userManufacturerInfoIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl)
{
}
void BusAccessUnit::userManufacturerInfoResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t* info, bool status)
{
}
void BusAccessUnit::userManufacturerInfoAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t* info)
{
}
void BusAccessUnit::authorizeLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint32_t key, bool status)
{
}
void BusAccessUnit::authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint32_t key)
{
}
void BusAccessUnit::authorizeResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level, bool status)
{
}
void BusAccessUnit::authorizeAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level)
{
}
void BusAccessUnit::keyWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level, uint32_t key, bool status)
{
}
void BusAccessUnit::keyWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level, uint32_t key)
{
}
void BusAccessUnit::keyWriteResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level, bool status)
{
}
void BusAccessUnit::keyWriteAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level)
{
}
void BusAccessUnit::connectConfirm(uint16_t destination)
{
}
void BusAccessUnit::systemNetworkParameterReadIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint16_t objectType,
uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength)
{
}
void BusAccessUnit::domainAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* rfDoA,
const uint8_t* knxSerialNumber)
{
}
void BusAccessUnit::domainAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* knxSerialNumber)
{
}
void BusAccessUnit::systemNetworkParameterReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint16_t objectType,
uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength, bool status)
{
}
void BusAccessUnit::domainAddressSerialNumberWriteLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* rfDoA,
const uint8_t* knxSerialNumber, bool status)
{
}
void BusAccessUnit::domainAddressSerialNumberReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* knxSerialNumber, bool status)
{
}
void BusAccessUnit::propertyValueRead(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId,
uint8_t& numberOfElements, uint16_t startIndex,
uint8_t** data, uint32_t& length)
{
}
void BusAccessUnit::propertyValueWrite(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId,
uint8_t& numberOfElements, uint16_t startIndex,
uint8_t* data, uint32_t length)
{
}
void BusAccessUnit::beforeRestartCallback(BeforeRestartCallback func)
{
}
BeforeRestartCallback BusAccessUnit::beforeRestartCallback()
{
return 0;
}
void BusAccessUnit::functionPropertyCallback(FunctionPropertyCallback func)
{
}
FunctionPropertyCallback BusAccessUnit::functionPropertyCallback()
{
return 0;
}
void BusAccessUnit::functionPropertyStateCallback(FunctionPropertyCallback func)
{
}
FunctionPropertyCallback BusAccessUnit::functionPropertyStateCallback()
{
return 0;
}

View File

@ -0,0 +1,184 @@
#pragma once
#include <stdint.h>
#include "knx_types.h"
#include "interface_object.h"
typedef void (*BeforeRestartCallback)(void);
typedef bool (*FunctionPropertyCallback)(uint8_t objectIndex, uint8_t propertyId, uint8_t length, uint8_t* data, uint8_t* resultData, uint8_t& resultLength);
class BusAccessUnit
{
public:
virtual ~BusAccessUnit() {}
virtual void groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, bool status);
virtual void groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl);
virtual void groupValueReadResponseConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopTtype, const SecurityControl& secCtrl,
uint8_t* data, uint8_t dataLength, bool status);
virtual void groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl,
uint8_t* data, uint8_t dataLength);
virtual void groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl,
uint8_t* data, uint8_t dataLength, bool status);
virtual void groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl,
uint8_t* data, uint8_t dataLength);
virtual void individualAddressWriteLocalConfirm(AckType ack, HopCountType hopType, const SecurityControl& secCtrl,
uint16_t newaddress, bool status);
virtual void individualAddressWriteIndication(HopCountType hopType, const SecurityControl& secCtrl, uint16_t newaddress);
virtual void individualAddressReadLocalConfirm(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, bool status);
virtual void individualAddressReadIndication(HopCountType hopType, const SecurityControl& secCtrl);
virtual void individualAddressReadResponseConfirm(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, bool status);
virtual void individualAddressReadAppLayerConfirm(HopCountType hopType, const SecurityControl& secCtrl, uint16_t individualAddress);
virtual void individualAddressSerialNumberReadLocalConfirm(AckType ack, HopCountType hopType, const SecurityControl& secCtrl,
uint8_t* serialNumber, bool status);
virtual void individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* knxSerialNumber);
virtual void individualAddressSerialNumberReadResponseConfirm(AckType ack, HopCountType hopType, const SecurityControl& secCtrl,
uint8_t* serialNumber, uint16_t domainAddress, bool status);
virtual void individualAddressSerialNumberReadAppLayerConfirm(HopCountType hopType, const SecurityControl& secCtrl, uint8_t* serialNumber,
uint16_t individualAddress, uint16_t domainAddress);
virtual void individualAddressSerialNumberWriteLocalConfirm(AckType ack, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* serialNumber,
uint16_t newaddress, bool status);
virtual void individualAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint16_t newIndividualAddress,
uint8_t* knxSerialNumber);
virtual void deviceDescriptorReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint8_t descriptorType, bool status);
virtual void deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t descriptorType);
virtual void deviceDescriptorReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint8_t descriptor_type, uint8_t* device_descriptor, bool status);
virtual void deviceDescriptorReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint8_t descriptortype, uint8_t* deviceDescriptor);
virtual void restartRequestLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, bool status);
virtual void restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, RestartType restartType, EraseCode eraseCode, uint8_t channel);
virtual void propertyValueReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, bool status);
virtual void propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex);
virtual void propertyValueExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ObjectType objectType, uint8_t objectInstance,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex);
virtual void functionPropertyCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex,
uint8_t propertyId, uint8_t* data, uint8_t length);
virtual void functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex,
uint8_t propertyId, uint8_t* data, uint8_t length);
virtual void functionPropertyExtCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ObjectType objectType, uint8_t objectInstance,
uint8_t propertyId, uint8_t* data, uint8_t length);
virtual void functionPropertyExtStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ObjectType objectType, uint8_t objectInstance,
uint8_t propertyId, uint8_t* data, uint8_t length);
virtual void propertyValueReadResponseConfirm(AckType ack, 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, bool status);
virtual void propertyValueReadAppLayerConfirm(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);
virtual void propertyValueWriteLocalConfirm(AckType ack, 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, bool status);
virtual 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);
virtual void propertyValueExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ObjectType objectType, uint8_t objectInstance,
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);
virtual void propertyDescriptionReadResponseConfirm(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, bool status);
virtual void propertyDescriptionReadAppLayerConfirm(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);
virtual void memoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress, bool status);
virtual void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint16_t memoryAddress);
virtual void memoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t* data, bool status);
virtual void memoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t* data);
virtual void memoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
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);
virtual void memoryExtReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint32_t memoryAddress, uint8_t* data, bool status);
virtual void memoryExtReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint32_t memoryAddress, uint8_t* data);
virtual void memoryExtWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint32_t memoryAddress, uint8_t* data, bool status);
virtual void memoryExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint32_t memoryAddress, uint8_t* data);
virtual void memoryExtWriteResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint32_t memoryAddress, uint8_t* data, bool status);
virtual void memoryExtWriteAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint32_t memoryAddress, uint8_t* data);
virtual void userMemoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint32_t memoryAddress, bool status);
virtual void userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint32_t memoryAddress);
virtual void userMemoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint32_t memoryAddress, uint8_t* memoryData, bool status);
virtual void userMemoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint32_t memoryAddress, uint8_t* memoryData);
virtual void userMemoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint32_t memoryAddress, uint8_t* memoryData, bool status);
virtual void userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint32_t memoryAddress, uint8_t* memoryData);
virtual void userManufacturerInfoLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, bool status);
virtual void userManufacturerInfoIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl);
virtual void userManufacturerInfoResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint8_t* info, bool status);
virtual void userManufacturerInfoAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl,
uint8_t* info);
virtual void authorizeLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint32_t key, bool status);
virtual void authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint32_t key);
virtual void authorizeResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level,
bool status);
virtual void authorizeAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level);
virtual void keyWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level,
uint32_t key, bool status);
virtual void keyWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level,
uint32_t key);
virtual void keyWriteResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level,
bool status);
virtual void keyWriteAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t level);
virtual void connectConfirm(uint16_t destination);
virtual void systemNetworkParameterReadIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint16_t objectType,
uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength);
virtual void domainAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* rfDoA,
const uint8_t* knxSerialNumber);
virtual void domainAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* knxSerialNumber);
virtual void systemNetworkParameterReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint16_t objectType,
uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength, bool status);
virtual void domainAddressSerialNumberWriteLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* rfDoA,
const uint8_t* knxSerialNumber, bool status);
virtual void domainAddressSerialNumberReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* knxSerialNumber, bool status);
virtual void propertyValueRead(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId,
uint8_t& numberOfElements, uint16_t startIndex,
uint8_t** data, uint32_t& length);
virtual void propertyValueWrite(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId,
uint8_t& numberOfElements, uint16_t startIndex,
uint8_t* data, uint32_t length);
virtual void beforeRestartCallback(BeforeRestartCallback func);
virtual BeforeRestartCallback beforeRestartCallback();
virtual void functionPropertyCallback(FunctionPropertyCallback func);
virtual FunctionPropertyCallback functionPropertyCallback();
virtual void functionPropertyStateCallback(FunctionPropertyCallback func);
virtual FunctionPropertyCallback functionPropertyStateCallback();
};

View File

@ -0,0 +1,178 @@
#include "config.h"
#if MASK_VERSION == 0x07B0
#include "bau07B0.h"
#include "bits.h"
#include <string.h>
#include <stdio.h>
using namespace std;
Bau07B0::Bau07B0(Platform& platform)
: BauSystemBDevice(platform), DataLinkLayerCallbacks(),
_dlLayer(_deviceObj, _netLayer.getInterface(), _platform, (ITpUartCallBacks&) * this, (DataLinkLayerCallbacks*) this)
#ifdef USE_CEMI_SERVER
, _cemiServer(*this)
#endif
{
_netLayer.getInterface().dataLinkLayer(_dlLayer);
#ifdef USE_CEMI_SERVER
_cemiServerObject.setMediumTypeAsSupported(DptMedium::KNX_TP1);
_cemiServer.dataLinkLayer(_dlLayer);
_dlLayer.cemiServer(_cemiServer);
_memory.addSaveRestore(&_cemiServerObject);
#endif
// Set Mask Version in Device Object depending on the BAU
_deviceObj.maskVersion(0x07B0);
// Set which interface objects are available in the device object
// This differs from BAU to BAU with different medium types.
// See PID_IO_LIST
Property* prop = _deviceObj.property(PID_IO_LIST);
prop->write(1, (uint16_t) OT_DEVICE);
prop->write(2, (uint16_t) OT_ADDR_TABLE);
prop->write(3, (uint16_t) OT_ASSOC_TABLE);
prop->write(4, (uint16_t) OT_GRP_OBJ_TABLE);
prop->write(5, (uint16_t) OT_APPLICATION_PROG);
#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER)
prop->write(6, (uint16_t) OT_SECURITY);
prop->write(7, (uint16_t) OT_CEMI_SERVER);
#elif defined(USE_DATASECURE)
prop->write(6, (uint16_t) OT_SECURITY);
#elif defined(USE_CEMI_SERVER)
prop->write(6, (uint16_t) OT_CEMI_SERVER);
#endif
}
InterfaceObject* Bau07B0::getInterfaceObject(uint8_t idx)
{
switch (idx)
{
case 0:
return &_deviceObj;
case 1:
return &_addrTable;
case 2:
return &_assocTable;
case 3:
return &_groupObjTable;
case 4:
return &_appProgram;
case 5: // would be app_program 2
return nullptr;
#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER)
case 6:
return &_secIfObj;
case 7:
return &_cemiServerObject;
#elif defined(USE_CEMI_SERVER)
case 6:
return &_cemiServerObject;
#elif defined(USE_DATASECURE)
case 6:
return &_secIfObj;
#endif
default:
return nullptr;
}
}
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
(void) objectInstance;
switch (objectType)
{
case OT_DEVICE:
return &_deviceObj;
case OT_ADDR_TABLE:
return &_addrTable;
case OT_ASSOC_TABLE:
return &_assocTable;
case OT_GRP_OBJ_TABLE:
return &_groupObjTable;
case OT_APPLICATION_PROG:
return &_appProgram;
#ifdef USE_DATASECURE
case OT_SECURITY:
return &_secIfObj;
#endif
#ifdef USE_CEMI_SERVER
case OT_CEMI_SERVER:
return &_cemiServerObject;
#endif
default:
return nullptr;
}
}
bool Bau07B0::enabled()
{
return _dlLayer.enabled();
}
void Bau07B0::enabled(bool value)
{
_dlLayer.enabled(value);
}
void Bau07B0::loop()
{
_dlLayer.loop();
BauSystemBDevice::loop();
#ifdef USE_CEMI_SERVER
_cemiServer.loop();
#endif
}
TPAckType Bau07B0::isAckRequired(uint16_t address, bool isGrpAddr)
{
if (isGrpAddr)
{
// ACK for broadcasts
if (address == 0)
return TPAckType::AckReqAck;
// is group address in group address table? ACK if yes.
if (_addrTable.contains(address))
return TPAckType::AckReqAck;
else
return TPAckType::AckReqNone;
}
// Also ACK for our own individual address
if (address == _deviceObj.individualAddress())
return TPAckType::AckReqAck;
if (address == 0)
{
println("Invalid broadcast detected: destination address is 0, but address type is \"individual\"");
}
return TPAckType::AckReqNone;
}
TpUartDataLinkLayer* Bau07B0::getDataLinkLayer()
{
return (TpUartDataLinkLayer*)&_dlLayer;
}
#endif

View File

@ -0,0 +1,34 @@
#pragma once
#include "config.h"
#if MASK_VERSION == 0x07B0
#include "bau_systemB_device.h"
#include "tpuart_data_link_layer.h"
#include "cemi_server.h"
#include "cemi_server_object.h"
class Bau07B0 : public BauSystemBDevice, public ITpUartCallBacks, public DataLinkLayerCallbacks
{
public:
Bau07B0(Platform& platform);
void loop() override;
bool enabled() override;
void enabled(bool value) override;
TpUartDataLinkLayer* getDataLinkLayer();
protected:
InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint16_t objectInstance);
// For TP1 only
TPAckType isAckRequired(uint16_t address, bool isGrpAddr) override;
private:
TpUartDataLinkLayer _dlLayer;
#ifdef USE_CEMI_SERVER
CemiServer _cemiServer;
CemiServerObject _cemiServerObject;
#endif
};
#endif

View File

@ -0,0 +1,262 @@
#include "config.h"
#if MASK_VERSION == 0x091A
#include "bau091A.h"
#include "bits.h"
#include <string.h>
#include <stdio.h>
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), 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)
#ifdef USE_CEMI_SERVER
, _cemiServer(*this)
#endif
{
// Before accessing anything of the router object they have to be initialized according to the used medium
// Coupler model 1.x
_routerObj.initialize1x(DptMedium::KNX_IP, 220);
// Mask 091A uses older coupler model 1.x which only uses one router object
_netLayer.rtObj(_routerObj);
_netLayer.getPrimaryInterface().dataLinkLayer(_dlLayerPrimary);
_netLayer.getSecondaryInterface().dataLinkLayer(_dlLayerSecondary);
#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);
_memory.addSaveRestore(&_ipParameters);
// Set Mask Version in Device Object depending on the BAU
_deviceObj.maskVersion(0x091A);
// Set which interface objects are available in the device object
// This differs from BAU to BAU with different medium types.
// See PID_IO_LIST
Property* prop = _deviceObj.property(PID_IO_LIST);
prop->write(1, (uint16_t) OT_DEVICE);
prop->write(2, (uint16_t) OT_ROUTER);
prop->write(3, (uint16_t) OT_APPLICATION_PROG);
prop->write(4, (uint16_t) OT_IP_PARAMETER);
#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER)
prop->write(5, (uint16_t) OT_SECURITY);
prop->write(6, (uint16_t) OT_CEMI_SERVER);
#elif defined(USE_DATASECURE)
prop->write(5, (uint16_t) OT_SECURITY);
#elif defined(USE_CEMI_SERVER)
prop->write(5, (uint16_t) OT_CEMI_SERVER);
#endif
}
InterfaceObject* Bau091A::getInterfaceObject(uint8_t idx)
{
switch (idx)
{
case 0:
return &_deviceObj;
case 1:
return &_routerObj;
case 2:
return &_appProgram;
case 3:
return &_ipParameters;
#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER)
case 4:
return &_secIfObj;
case 5:
return &_cemiServerObject;
#elif defined(USE_CEMI_SERVER)
case 4:
return &_cemiServerObject;
#elif defined(USE_DATASECURE)
case 4:
return &_secIfObj;
#endif
default:
return nullptr;
}
}
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
(void) objectInstance;
switch (objectType)
{
case OT_DEVICE:
return &_deviceObj;
case OT_ROUTER:
return &_routerObj;
case OT_APPLICATION_PROG:
return &_appProgram;
case OT_IP_PARAMETER:
return &_ipParameters;
#ifdef USE_DATASECURE
case OT_SECURITY:
return &_secIfObj;
#endif
#ifdef USE_CEMI_SERVER
case OT_CEMI_SERVER:
return &_cemiServerObject;
#endif
default:
return nullptr;
}
}
void Bau091A::doMasterReset(EraseCode eraseCode, uint8_t channel)
{
// Common SystemB objects
BauSystemBCoupler::doMasterReset(eraseCode, channel);
_ipParameters.masterReset(eraseCode, channel);
_routerObj.masterReset(eraseCode, channel);
}
bool Bau091A::enabled()
{
return _dlLayerPrimary.enabled() && _dlLayerSecondary.enabled();
}
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()
{
_dlLayerPrimary.loop();
_dlLayerSecondary.loop();
BauSystemBCoupler::loop();
}
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)
{
ack = TPAckType::AckReqAck;
}
else
{
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
{
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 ack;
}
bool Bau091A::configured()
{
// _configured is set to true initially, if the device was configured with ETS it will be set to true after restart
if (!_configured)
return false;
_configured = _routerObj.loadState() == LS_LOADED;
#ifdef USE_DATASECURE
_configured &= _secIfObj.loadState() == LS_LOADED;
#endif
return _configured;
}
IpDataLinkLayer* Bau091A::getPrimaryDataLinkLayer()
{
return (IpDataLinkLayer*)&_dlLayerPrimary;
}
TpUartDataLinkLayer* Bau091A::getSecondaryDataLinkLayer()
{
return (TpUartDataLinkLayer*)&_dlLayerSecondary;
}
#endif

View File

@ -0,0 +1,42 @@
#pragma once
#include "config.h"
#if MASK_VERSION == 0x091A
#include "bau_systemB_coupler.h"
#include "router_object.h"
#include "ip_parameter_object.h"
#include "ip_data_link_layer.h"
#include "tpuart_data_link_layer.h"
#include "cemi_server_object.h"
class Bau091A : public BauSystemBCoupler, public ITpUartCallBacks, public DataLinkLayerCallbacks
{
public:
Bau091A(Platform& platform);
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, uint16_t objectInstance);
// For TP1 only
TPAckType isAckRequired(uint16_t address, bool isGrpAddr) override;
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
private:
RouterObject _routerObj;
IpParameterObject _ipParameters;
IpDataLinkLayer _dlLayerPrimary;
TpUartDataLinkLayer _dlLayerSecondary;
#ifdef USE_CEMI_SERVER
CemiServer _cemiServer;
CemiServerObject _cemiServerObject;
#endif
};
#endif

View File

@ -0,0 +1,207 @@
#include "config.h"
#if MASK_VERSION == 0x27B0
#include "bau27B0.h"
#include "bits.h"
#include <string.h>
#include <stdio.h>
using namespace std;
Bau27B0::Bau27B0(Platform& platform)
: BauSystemBDevice(platform),
_dlLayer(_deviceObj, _rfMediumObj, _netLayer.getInterface(), _platform)
#ifdef USE_CEMI_SERVER
, _cemiServer(*this)
#endif
{
_netLayer.getInterface().dataLinkLayer(_dlLayer);
_memory.addSaveRestore(&_rfMediumObj);
#ifdef USE_CEMI_SERVER
_cemiServerObject.setMediumTypeAsSupported(DptMedium::KNX_RF);
_cemiServer.dataLinkLayer(_dlLayer);
_dlLayer.cemiServer(_cemiServer);
_memory.addSaveRestore(&_cemiServerObject);
#endif
// Set Mask Version in Device Object depending on the BAU
_deviceObj.maskVersion(0x27B0);
// Set the maximum APDU length
// ETS will consider this value while programming the device
// For KNX-RF we use a smallest allowed value for now,
// although long frame are also supported by the implementation.
// Needs some experimentation.
_deviceObj.maxApduLength(15);
// Set which interface objects are available in the device object
// This differs from BAU to BAU with different medium types.
// See PID_IO_LIST
Property* prop = _deviceObj.property(PID_IO_LIST);
prop->write(1, (uint16_t) OT_DEVICE);
prop->write(2, (uint16_t) OT_ADDR_TABLE);
prop->write(3, (uint16_t) OT_ASSOC_TABLE);
prop->write(4, (uint16_t) OT_GRP_OBJ_TABLE);
prop->write(5, (uint16_t) OT_APPLICATION_PROG);
prop->write(6, (uint16_t) OT_RF_MEDIUM);
#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER)
prop->write(7, (uint16_t) OT_SECURITY);
prop->write(8, (uint16_t) OT_CEMI_SERVER);
#elif defined(USE_DATASECURE)
prop->write(7, (uint16_t) OT_SECURITY);
#elif defined(USE_CEMI_SERVER)
prop->write(7, (uint16_t)OT_CEMI_SERVER);
#endif
}
// see KNX AN160 p.74 for mask 27B0
InterfaceObject* Bau27B0::getInterfaceObject(uint8_t idx)
{
switch (idx)
{
case 0:
return &_deviceObj;
case 1:
return &_addrTable;
case 2:
return &_assocTable;
case 3:
return &_groupObjTable;
case 4:
return &_appProgram;
case 5: // would be app_program 2
return nullptr;
case 6:
return &_rfMediumObj;
#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER)
case 7:
return &_secIfObj;
case 8:
return &_cemiServerObject;
#elif defined(USE_CEMI_SERVER)
case 7:
return &_cemiServerObject;
#elif defined(USE_DATASECURE)
case 7:
return &_secIfObj;
#endif
default:
return nullptr;
}
}
InterfaceObject* 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
(void) objectInstance;
switch (objectType)
{
case OT_DEVICE:
return &_deviceObj;
case OT_ADDR_TABLE:
return &_addrTable;
case OT_ASSOC_TABLE:
return &_assocTable;
case OT_GRP_OBJ_TABLE:
return &_groupObjTable;
case OT_APPLICATION_PROG:
return &_appProgram;
case OT_RF_MEDIUM:
return &_rfMediumObj;
#ifdef USE_DATASECURE
case OT_SECURITY:
return &_secIfObj;
#endif
#ifdef USE_CEMI_SERVER
case OT_CEMI_SERVER:
return &_cemiServerObject;
#endif
default:
return nullptr;
}
}
void Bau27B0::doMasterReset(EraseCode eraseCode, uint8_t channel)
{
// Common SystemB objects
BauSystemB::doMasterReset(eraseCode, channel);
_rfMediumObj.masterReset(eraseCode, channel);
}
bool Bau27B0::enabled()
{
return _dlLayer.enabled();
}
void Bau27B0::enabled(bool value)
{
_dlLayer.enabled(value);
}
void Bau27B0::loop()
{
_dlLayer.loop();
BauSystemBDevice::loop();
#ifdef USE_CEMI_SERVER
_cemiServer.loop();
#endif
}
void Bau27B0::domainAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* rfDoA,
const uint8_t* knxSerialNumber)
{
// If the received serial number matches our serial number
// then store the received RF domain address in the RF medium object
if (!memcmp(knxSerialNumber, _deviceObj.propertyData(PID_SERIAL_NUMBER), 6))
_rfMediumObj.rfDomainAddress(rfDoA);
}
void Bau27B0::domainAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* knxSerialNumber)
{
// If the received serial number matches our serial number
// then send a response with the current RF domain address stored in the RF medium object
if (!memcmp(knxSerialNumber, _deviceObj.propertyData(PID_SERIAL_NUMBER), 6))
_appLayer.domainAddressSerialNumberReadResponse(priority, hopType, secCtrl, _rfMediumObj.rfDomainAddress(), knxSerialNumber);
}
void Bau27B0::individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* knxSerialNumber)
{
#pragma warning "individualAddressSerialNumberReadIndication is not available for rf"
}
void Bau27B0::domainAddressSerialNumberWriteLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* rfDoA,
const uint8_t* knxSerialNumber, bool status)
{
}
void Bau27B0::domainAddressSerialNumberReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* knxSerialNumber, bool status)
{
}
RfDataLinkLayer* Bau27B0::getDataLinkLayer()
{
return (RfDataLinkLayer*)&_dlLayer;
}
#endif // #ifdef USE_RF

View File

@ -0,0 +1,47 @@
#pragma once
#include "config.h"
#if MASK_VERSION == 0x27B0
#include "bau_systemB_device.h"
#include "rf_medium_object.h"
#if defined(DeviceFamily_CC13X0)
#include "rf_physical_layer_cc1310.h"
#else
#include "rf_physical_layer_cc1101.h"
#endif
#include "rf_data_link_layer.h"
#include "cemi_server.h"
#include "cemi_server_object.h"
class Bau27B0 : public BauSystemBDevice
{
public:
Bau27B0(Platform& platform);
void loop() override;
bool enabled() override;
void enabled(bool value) override;
RfDataLinkLayer* getDataLinkLayer();
protected:
InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint16_t objectInstance);
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
private:
RfDataLinkLayer _dlLayer;
RfMediumObject _rfMediumObj;
#ifdef USE_CEMI_SERVER
CemiServer _cemiServer;
CemiServerObject _cemiServerObject;
#endif
void domainAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* rfDoA,
const uint8_t* knxSerialNumber) override;
void domainAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* knxSerialNumber) override;
void individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* knxSerialNumber) override;
void domainAddressSerialNumberWriteLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* rfDoA,
const uint8_t* knxSerialNumber, bool status) override;
void domainAddressSerialNumberReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* knxSerialNumber, bool status) override;
};
#endif

View File

@ -0,0 +1,181 @@
#include "config.h"
#if MASK_VERSION == 0x2920
#include "bau2920.h"
#include "bits.h"
#include <string.h>
#include <stdio.h>
using namespace std;
// Mask 0x2920 uses coupler model 2.0
Bau2920::Bau2920(Platform& platform)
: BauSystemBCoupler(platform),
_rtObjPrimary(memory()),
_rtObjSecondary(memory()),
_rfMediumObject(),
_dlLayerPrimary(_deviceObj, _netLayer.getPrimaryInterface(), _platform, (ITpUartCallBacks&) * this),
_dlLayerSecondary(_deviceObj, _rfMediumObject, _netLayer.getSecondaryInterface(), platform)
#ifdef USE_CEMI_SERVER
,
_cemiServer(*this)
#endif
{
// Before accessing anything of the two router objects they have to be initialized according to the used media combination
// Coupler model 2.0
_rtObjPrimary.initialize20(1, DptMedium::KNX_TP1, RouterObjectType::Primary, 201);
_rtObjSecondary.initialize20(2, DptMedium::KNX_RF, RouterObjectType::Secondary, 201);
_netLayer.rtObjPrimary(_rtObjPrimary);
_netLayer.rtObjSecondary(_rtObjSecondary);
_netLayer.getPrimaryInterface().dataLinkLayer(_dlLayerPrimary);
_netLayer.getSecondaryInterface().dataLinkLayer(_dlLayerSecondary);
#ifdef USE_CEMI_SERVER
_cemiServerObject.setMediumTypeAsSupported(DptMedium::KNX_TP1);
_cemiServerObject.setMediumTypeAsSupported(DptMedium::KNX_RF);
_cemiServer.dataLinkLayer(_dlLayerSecondary); // Secondary I/F is the important one!
_dlLayerSecondary.cemiServer(_cemiServer);
_memory.addSaveRestore(&_cemiServerObject);
#endif
_memory.addSaveRestore(&_rtObjPrimary);
_memory.addSaveRestore(&_rtObjSecondary);
_memory.addSaveRestore(&_rfMediumObject);
// Set Mask Version in Device Object depending on the BAU
_deviceObj.maskVersion(0x2920);
// Set which interface objects are available in the device object
// This differs from BAU to BAU with different medium types.
// See PID_IO_LIST
Property* prop = _deviceObj.property(PID_IO_LIST);
prop->write(1, (uint16_t) OT_DEVICE);
prop->write(2, (uint16_t) OT_ROUTER);
prop->write(3, (uint16_t) OT_ROUTER);
prop->write(4, (uint16_t) OT_APPLICATION_PROG);
prop->write(5, (uint16_t) OT_RF_MEDIUM);
#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER)
prop->write(6, (uint16_t) OT_SECURITY);
prop->write(7, (uint16_t) OT_CEMI_SERVER);
#elif defined(USE_DATASECURE)
prop->write(6, (uint16_t) OT_SECURITY);
#elif defined(USE_CEMI_SERVER)
prop->write(6, (uint16_t) OT_CEMI_SERVER);
#endif
}
InterfaceObject* Bau2920::getInterfaceObject(uint8_t idx)
{
switch (idx)
{
case 0:
return &_deviceObj;
case 1:
return &_rtObjPrimary;
case 2:
return &_rtObjSecondary;
case 3:
return &_appProgram;
case 4:
return &_rfMediumObject;
#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER)
case 5:
return &_secIfObj;
case 6:
return &_cemiServerObject;
#elif defined(USE_CEMI_SERVER)
case 5:
return &_cemiServerObject;
#elif defined(USE_DATASECURE)
case 5:
return &_secIfObj;
#endif
default:
return nullptr;
}
}
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
(void) objectInstance;
switch (objectType)
{
case OT_DEVICE:
return &_deviceObj;
case OT_ROUTER:
return objectInstance == 0 ? &_rtObjPrimary : &_rtObjSecondary;
case OT_APPLICATION_PROG:
return &_appProgram;
case OT_RF_MEDIUM:
return &_rfMediumObject;
#ifdef USE_DATASECURE
case OT_SECURITY:
return &_secIfObj;
#endif
#ifdef USE_CEMI_SERVER
case OT_CEMI_SERVER:
return &_cemiServerObject;
#endif
default:
return nullptr;
}
}
void Bau2920::doMasterReset(EraseCode eraseCode, uint8_t channel)
{
// Common SystemB objects
BauSystemBCoupler::doMasterReset(eraseCode, channel);
_rfMediumObject.masterReset(eraseCode, channel);
_rtObjPrimary.masterReset(eraseCode, channel);
_rtObjSecondary.masterReset(eraseCode, channel);
}
bool Bau2920::enabled()
{
return _dlLayerPrimary.enabled() && _dlLayerSecondary.enabled();
}
void Bau2920::enabled(bool value)
{
_dlLayerPrimary.enabled(value);
_dlLayerSecondary.enabled(value);
}
void Bau2920::loop()
{
_dlLayerPrimary.loop();
_dlLayerSecondary.loop();
BauSystemBCoupler::loop();
}
TpUartDataLinkLayer* Bau2920::getPrimaryDataLinkLayer()
{
return (TpUartDataLinkLayer*)&_dlLayerPrimary;
}
RfDataLinkLayer* Bau2920::getSecondaryDataLinkLayer()
{
return (RfDataLinkLayer*)&_dlLayerSecondary;
}
#endif

View File

@ -0,0 +1,43 @@
#pragma once
#include "config.h"
#if MASK_VERSION == 0x2920
#include "bau_systemB_coupler.h"
#include "tpuart_data_link_layer.h"
#if defined(DeviceFamily_CC13X0)
#include "rf_physical_layer_cc1310.h"
#else
#include "rf_physical_layer_cc1101.h"
#endif
#include "rf_data_link_layer.h"
#include "rf_medium_object.h"
#include "cemi_server_object.h"
class Bau2920 : public BauSystemBCoupler
{
public:
Bau2920(Platform& platform);
void loop() override;
bool enabled() override;
void enabled(bool value) override;
TpUartDataLinkLayer* getPrimaryDataLinkLayer();
RfDataLinkLayer* getSecondaryDataLinkLayer();
protected:
InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint16_t objectInstance);
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
private:
RouterObject _rtObjPrimary;
RouterObject _rtObjSecondary;
RfMediumObject _rfMediumObject;
TpUartDataLinkLayer _dlLayerPrimary;
RfDataLinkLayer _dlLayerSecondary;
#ifdef USE_CEMI_SERVER
CemiServer _cemiServer;
CemiServerObject _cemiServerObject;
#endif
};
#endif

View File

@ -0,0 +1,169 @@
#include "config.h"
#if MASK_VERSION == 0x57B0
#include "bau57B0.h"
#include "bits.h"
#include <string.h>
#include <stdio.h>
using namespace std;
Bau57B0::Bau57B0(Platform& platform)
: BauSystemBDevice(platform), DataLinkLayerCallbacks(),
_ipParameters(_deviceObj, platform),
_dlLayer(_deviceObj, _ipParameters, _netLayer.getInterface(), _platform, (DataLinkLayerCallbacks*) this)
#ifdef USE_CEMI_SERVER
, _cemiServer(*this)
#endif
{
_netLayer.getInterface().dataLinkLayer(_dlLayer);
#ifdef USE_CEMI_SERVER
_cemiServerObject.setMediumTypeAsSupported(DptMedium::KNX_IP);
_cemiServer.dataLinkLayer(_dlLayer);
_dlLayer.cemiServer(_cemiServer);
_memory.addSaveRestore(&_cemiServerObject);
#endif
_memory.addSaveRestore(&_ipParameters);
// Set Mask Version in Device Object depending on the BAU
_deviceObj.maskVersion(0x57B0);
// Set which interface objects are available in the device object
// This differs from BAU to BAU with different medium types.
// See PID_IO_LIST
Property* prop = _deviceObj.property(PID_IO_LIST);
prop->write(1, (uint16_t) OT_DEVICE);
prop->write(2, (uint16_t) OT_ADDR_TABLE);
prop->write(3, (uint16_t) OT_ASSOC_TABLE);
prop->write(4, (uint16_t) OT_GRP_OBJ_TABLE);
prop->write(5, (uint16_t) OT_APPLICATION_PROG);
prop->write(6, (uint16_t) OT_IP_PARAMETER);
#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER)
prop->write(7, (uint16_t) OT_SECURITY);
prop->write(8, (uint16_t) OT_CEMI_SERVER);
#elif defined(USE_DATASECURE)
prop->write(7, (uint16_t) OT_SECURITY);
#elif defined(USE_CEMI_SERVER)
prop->write(7, (uint16_t) OT_CEMI_SERVER);
#endif
}
InterfaceObject* Bau57B0::getInterfaceObject(uint8_t idx)
{
switch (idx)
{
case 0:
return &_deviceObj;
case 1:
return &_addrTable;
case 2:
return &_assocTable;
case 3:
return &_groupObjTable;
case 4:
return &_appProgram;
case 5: // would be app_program 2
return nullptr;
case 6:
return &_ipParameters;
#if defined(USE_DATASECURE) && defined(USE_CEMI_SERVER)
case 7:
return &_secIfObj;
case 8:
return &_cemiServerObject;
#elif defined(USE_CEMI_SERVER)
case 7:
return &_cemiServerObject;
#elif defined(USE_DATASECURE)
case 7:
return &_secIfObj;
#endif
default:
return nullptr;
}
}
InterfaceObject* 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
(void) objectInstance;
switch (objectType)
{
case OT_DEVICE:
return &_deviceObj;
case OT_ADDR_TABLE:
return &_addrTable;
case OT_ASSOC_TABLE:
return &_assocTable;
case OT_GRP_OBJ_TABLE:
return &_groupObjTable;
case OT_APPLICATION_PROG:
return &_appProgram;
case OT_IP_PARAMETER:
return &_ipParameters;
#ifdef USE_DATASECURE
case OT_SECURITY:
return &_secIfObj;
#endif
#ifdef USE_CEMI_SERVER
case OT_CEMI_SERVER:
return &_cemiServerObject;
#endif
default:
return nullptr;
}
}
void Bau57B0::doMasterReset(EraseCode eraseCode, uint8_t channel)
{
// Common SystemB objects
BauSystemB::doMasterReset(eraseCode, channel);
_ipParameters.masterReset(eraseCode, channel);
}
bool Bau57B0::enabled()
{
return _dlLayer.enabled();
}
void Bau57B0::enabled(bool value)
{
_dlLayer.enabled(value);
}
void Bau57B0::loop()
{
_dlLayer.loop();
BauSystemBDevice::loop();
#ifdef USE_CEMI_SERVER
_cemiServer.loop();
#endif
}
IpDataLinkLayer* Bau57B0::getDataLinkLayer()
{
return (IpDataLinkLayer*)&_dlLayer;
}
#endif

View File

@ -0,0 +1,33 @@
#pragma once
#include "config.h"
#if MASK_VERSION == 0x57B0
#include "bau_systemB_device.h"
#include "ip_parameter_object.h"
#include "ip_data_link_layer.h"
#include "cemi_server_object.h"
class Bau57B0 : public BauSystemBDevice, public DataLinkLayerCallbacks
{
public:
Bau57B0(Platform& platform);
void loop() override;
bool enabled() override;
void enabled(bool value) override;
IpDataLinkLayer* getDataLinkLayer();
protected:
InterfaceObject* getInterfaceObject(uint8_t idx);
InterfaceObject* getInterfaceObject(ObjectType objectType, uint16_t objectInstance);
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
private:
IpParameterObject _ipParameters;
IpDataLinkLayer _dlLayer;
#ifdef USE_CEMI_SERVER
CemiServer _cemiServer;
CemiServerObject _cemiServerObject;
#endif
};
#endif

View File

@ -0,0 +1,812 @@
#include "bau_systemB.h"
#include "bits.h"
#include <string.h>
#include <stdio.h>
enum NmReadSerialNumberType
{
NM_Read_SerialNumber_By_ProgrammingMode = 0x01,
NM_Read_SerialNumber_By_ExFactoryState = 0x02,
NM_Read_SerialNumber_By_PowerReset = 0x03,
NM_Read_SerialNumber_By_ManufacturerSpecific = 0xFE,
};
static constexpr auto kFunctionPropertyResultBufferMaxSize = 0xFF;
static constexpr auto kRestartProcessTime = 3;
BauSystemB::BauSystemB(Platform& platform): _memory(platform, _deviceObj),
_appProgram(_memory),
_platform(platform)
{
_memory.addSaveRestore(&_appProgram);
}
void BauSystemB::readMemory()
{
_memory.readMemory();
}
void BauSystemB::writeMemory()
{
_memory.writeMemory();
}
Platform& BauSystemB::platform()
{
return _platform;
}
ApplicationProgramObject& BauSystemB::parameters()
{
return _appProgram;
}
DeviceObject& BauSystemB::deviceObject()
{
return _deviceObj;
}
uint8_t BauSystemB::checkmasterResetValidity(EraseCode eraseCode, uint8_t channel)
{
static constexpr uint8_t successCode = 0x00; // Where does this come from? It is the code for "success".
static constexpr uint8_t invalidEraseCode = 0x02; // Where does this come from? It is the error code for "unspported erase code".
switch (eraseCode)
{
case EraseCode::ConfirmedRestart:
{
println("Confirmed restart requested.");
return successCode;
}
case EraseCode::ResetAP:
{
// TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER)
println("ResetAP requested. Not implemented yet.");
return successCode;
}
case EraseCode::ResetIA:
{
// TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER)
println("ResetIA requested. Not implemented yet.");
return successCode;
}
case EraseCode::ResetLinks:
{
// TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER)
println("ResetLinks requested. Not implemented yet.");
return successCode;
}
case EraseCode::ResetParam:
{
// TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER)
println("ResetParam requested. Not implemented yet.");
return successCode;
}
case EraseCode::FactoryReset:
{
// TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER)
println("Factory reset requested. type: with IA");
return successCode;
}
case EraseCode::FactoryResetWithoutIA:
{
// TODO: increase download counter except for confirmed restart (PID_DOWNLOAD_COUNTER)
println("Factory reset requested. type: without IA");
return successCode;
}
default:
{
print("Unhandled erase code: ");
println(eraseCode, HEX);
return invalidEraseCode;
}
}
}
void BauSystemB::deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t descriptorType)
{
if (descriptorType != 0)
descriptorType = 0x3f;
uint8_t data[2];
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)
{
_memory.writeMemory(memoryAddress, number, data);
if (_deviceObj.verifyMode())
memoryReadIndication(priority, hopType, asap, secCtrl, number, memoryAddress, data);
}
void BauSystemB::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress, uint8_t* data)
{
applicationLayer().memoryReadResponse(AckRequested, priority, hopType, asap, secCtrl, number, memoryAddress, data);
}
void BauSystemB::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
uint16_t memoryAddress)
{
applicationLayer().memoryReadResponse(AckRequested, priority, hopType, asap, secCtrl, number, memoryAddress,
_memory.toAbsolute(memoryAddress));
}
void BauSystemB::memoryExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* data)
{
_memory.writeMemory(memoryAddress, number, data);
applicationLayer().memoryExtWriteResponse(AckRequested, priority, hopType, asap, secCtrl, ReturnCodes::Success, number, memoryAddress, _memory.toAbsolute(memoryAddress));
}
void BauSystemB::memoryExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress)
{
applicationLayer().memoryExtReadResponse(AckRequested, priority, hopType, asap, secCtrl, ReturnCodes::Success, number, memoryAddress, _memory.toAbsolute(memoryAddress));
}
void BauSystemB::doMasterReset(EraseCode eraseCode, uint8_t channel)
{
_deviceObj.masterReset(eraseCode, channel);
_appProgram.masterReset(eraseCode, channel);
}
void BauSystemB::restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, RestartType restartType, EraseCode eraseCode, uint8_t channel)
{
if (restartType == RestartType::BasicRestart)
{
println("Basic restart requested");
if (_beforeRestart != 0)
_beforeRestart();
}
else if (restartType == RestartType::MasterReset)
{
uint8_t errorCode = checkmasterResetValidity(eraseCode, channel);
// We send the restart response now before actually applying the reset values
// Processing time is kRestartProcessTime (example 3 seconds) that we require for the applying the master reset with restart
applicationLayer().restartResponse(AckRequested, priority, hopType, secCtrl, errorCode, (errorCode == 0) ? kRestartProcessTime : 0);
doMasterReset(eraseCode, channel);
}
else
{
// Cannot happen as restartType is just one bit
println("Unhandled restart type.");
_platform.fatalError();
}
// Flush the EEPROM before resetting
_memory.writeMemory();
_platform.restart();
}
void BauSystemB::authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint32_t key)
{
applicationLayer().authorizeResponse(AckRequested, priority, hopType, asap, secCtrl, 0);
}
void BauSystemB::userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress)
{
applicationLayer().userMemoryReadResponse(AckRequested, priority, hopType, asap, secCtrl, number, memoryAddress,
_memory.toAbsolute(memoryAddress));
}
void BauSystemB::userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress, uint8_t* data)
{
_memory.writeMemory(memoryAddress, number, data);
if (_deviceObj.verifyMode())
userMemoryReadIndication(priority, hopType, asap, secCtrl, number, memoryAddress);
}
void BauSystemB::propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex,
uint8_t propertyId, uint8_t propertyIndex)
{
uint8_t pid = propertyId;
bool writeEnable = false;
uint8_t type = 0;
uint16_t numberOfElements = 0;
uint8_t access = 0;
InterfaceObject* obj = getInterfaceObject(objectIndex);
if (obj)
obj->readPropertyDescription(pid, propertyIndex, writeEnable, type, numberOfElements, access);
applicationLayer().propertyDescriptionReadResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, pid, propertyIndex,
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)
{
InterfaceObject* obj = getInterfaceObject(objectIndex);
if (obj)
obj->writeProperty((PropertyID)propertyId, startIndex, data, numberOfElements);
propertyValueReadIndication(priority, hopType, asap, secCtrl, objectIndex, propertyId, numberOfElements, startIndex);
}
void BauSystemB::propertyValueExtWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ObjectType objectType, uint8_t objectInstance,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool confirmed)
{
uint8_t returnCode = ReturnCodes::Success;
InterfaceObject* obj = getInterfaceObject(objectType, objectInstance);
if (obj)
obj->writeProperty((PropertyID)propertyId, startIndex, data, numberOfElements);
else
returnCode = ReturnCodes::AddressVoid;
if (confirmed)
{
applicationLayer().propertyValueExtWriteConResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, numberOfElements, startIndex, returnCode);
}
}
void BauSystemB::propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex)
{
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)
{
uint8_t elementSize = obj->propertySize((PropertyID)propertyId);
if (startIndex > 0)
size = elementSize * numberOfElements;
else
size = sizeof(uint16_t); // size of property array entry 0 which contains the current number of elements
}
else
elementCount = 0;
uint8_t data[size];
if (obj)
obj->readProperty((PropertyID)propertyId, startIndex, elementCount, data);
if (elementCount == 0)
size = 0;
applicationLayer().propertyValueReadResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, propertyId, elementCount,
startIndex, data, size);
}
void BauSystemB::propertyValueExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ObjectType objectType, uint8_t objectInstance,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex)
{
uint8_t size = 0;
uint8_t elementCount = numberOfElements;
InterfaceObject* obj = getInterfaceObject(objectType, objectInstance);
if (obj)
{
uint8_t elementSize = obj->propertySize((PropertyID)propertyId);
if (startIndex > 0)
size = elementSize * numberOfElements;
else
size = sizeof(uint16_t); // size of propert array entry 0 which is the size
}
else
elementCount = 0;
uint8_t data[size];
if (obj)
obj->readProperty((PropertyID)propertyId, startIndex, elementCount, data);
if (elementCount == 0)
size = 0;
applicationLayer().propertyValueExtReadResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, elementCount,
startIndex, data, size);
}
void BauSystemB::functionPropertyCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex,
uint8_t propertyId, uint8_t* data, uint8_t length)
{
uint8_t resultData[kFunctionPropertyResultBufferMaxSize];
uint8_t resultLength = sizeof(resultData); // tell the callee the maximum size of the buffer
bool handled = false;
InterfaceObject* obj = getInterfaceObject(objectIndex);
if (obj)
{
if (obj->property((PropertyID)propertyId)->Type() == PDT_FUNCTION)
{
obj->command((PropertyID)propertyId, data, length, resultData, resultLength);
handled = true;
}
else
{
if (_functionProperty != 0)
if (_functionProperty(objectIndex, propertyId, length, data, resultData, resultLength))
handled = true;
}
}
else
{
if (_functionProperty != 0)
if (_functionProperty(objectIndex, propertyId, length, data, resultData, resultLength))
handled = true;
}
//only return a value it was handled by a property or function
if (handled)
applicationLayer().functionPropertyStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, propertyId, resultData, resultLength);
}
void BauSystemB::functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex,
uint8_t propertyId, uint8_t* data, uint8_t length)
{
uint8_t resultData[kFunctionPropertyResultBufferMaxSize];
uint8_t resultLength = sizeof(resultData); // tell the callee the maximum size of the buffer
bool handled = true;
InterfaceObject* obj = getInterfaceObject(objectIndex);
if (obj)
{
if (obj->property((PropertyID)propertyId)->Type() == PDT_FUNCTION)
{
obj->state((PropertyID)propertyId, data, length, resultData, resultLength);
handled = true;
}
else
{
if (_functionPropertyState != 0)
if (_functionPropertyState(objectIndex, propertyId, length, data, resultData, resultLength))
handled = true;
}
}
else
{
if (_functionPropertyState != 0)
if (_functionPropertyState(objectIndex, propertyId, length, data, resultData, resultLength))
handled = true;
}
//only return a value it was handled by a property or function
if (handled)
applicationLayer().functionPropertyStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectIndex, propertyId, resultData, resultLength);
}
void BauSystemB::functionPropertyExtCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ObjectType objectType, uint8_t objectInstance,
uint8_t propertyId, uint8_t* data, uint8_t length)
{
uint8_t resultData[kFunctionPropertyResultBufferMaxSize];
uint8_t resultLength = 1; // we always have to include the return code at least
InterfaceObject* obj = getInterfaceObject(objectType, objectInstance);
if (obj)
{
PropertyDataType propType = obj->property((PropertyID)propertyId)->Type();
if (propType == PDT_FUNCTION)
{
// The first byte is reserved and 0 for PDT_FUNCTION
uint8_t reservedByte = data[0];
if (reservedByte != 0x00)
{
resultData[0] = ReturnCodes::DataVoid;
}
else
{
resultLength = sizeof(resultData); // tell the callee the maximum size of the buffer
obj->command((PropertyID)propertyId, data, length, resultData, resultLength);
// resultLength was modified by the callee
}
}
else if (propType == PDT_CONTROL)
{
uint8_t count = 1;
// write the event
obj->writeProperty((PropertyID)propertyId, 1, data, count);
if (count == 1)
{
// Read the current state (one byte only) for the response
obj->readProperty((PropertyID)propertyId, 1, count, &resultData[1]);
resultLength = count ? 2 : 1;
resultData[0] = count ? ReturnCodes::Success : ReturnCodes::DataVoid;
}
else
{
resultData[0] = ReturnCodes::AddressVoid;
}
}
else
{
resultData[0] = ReturnCodes::DataTypeConflict;
}
}
else
{
resultData[0] = ReturnCodes::GenericError;
}
applicationLayer().functionPropertyExtStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, resultData, resultLength);
}
void BauSystemB::functionPropertyExtStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ObjectType objectType, uint8_t objectInstance,
uint8_t propertyId, uint8_t* data, uint8_t length)
{
uint8_t resultData[kFunctionPropertyResultBufferMaxSize];
uint8_t resultLength = sizeof(resultData); // tell the callee the maximum size of the buffer
InterfaceObject* obj = getInterfaceObject(objectType, objectInstance);
if (obj)
{
PropertyDataType propType = obj->property((PropertyID)propertyId)->Type();
if (propType == PDT_FUNCTION)
{
// The first byte is reserved and 0 for PDT_FUNCTION
uint8_t reservedByte = data[0];
if (reservedByte != 0x00)
{
resultData[0] = ReturnCodes::DataVoid;
}
else
{
resultLength = sizeof(resultData); // tell the callee the maximum size of the buffer
obj->state((PropertyID)propertyId, data, length, resultData, resultLength);
// resultLength was modified by the callee
}
}
else if (propType == PDT_CONTROL)
{
uint8_t count = 1;
// Read the current state (one byte only) for the response
obj->readProperty((PropertyID)propertyId, 1, count, &resultData[1]);
resultLength = count ? 2 : 1;
resultData[0] = count ? ReturnCodes::Success : ReturnCodes::DataVoid;
}
else
{
resultData[0] = ReturnCodes::DataTypeConflict;
}
}
else
{
resultData[0] = ReturnCodes::GenericError;
}
applicationLayer().functionPropertyExtStateResponse(AckRequested, priority, hopType, asap, secCtrl, objectType, objectInstance, propertyId, resultData, resultLength);
}
void BauSystemB::individualAddressReadIndication(HopCountType hopType, const SecurityControl& secCtrl)
{
if (_deviceObj.progMode())
applicationLayer().individualAddressReadResponse(AckRequested, hopType, secCtrl);
}
void BauSystemB::individualAddressWriteIndication(HopCountType hopType, const SecurityControl& secCtrl, uint16_t newaddress)
{
if (_deviceObj.progMode())
_deviceObj.individualAddress(newaddress);
}
void BauSystemB::individualAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint16_t newIndividualAddress,
uint8_t* knxSerialNumber)
{
// If the received serial number matches our serial number
// then store the received new individual address in the device object
if (!memcmp(knxSerialNumber, _deviceObj.propertyData(PID_SERIAL_NUMBER), 6))
_deviceObj.individualAddress(newIndividualAddress);
}
void BauSystemB::individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* knxSerialNumber)
{
// If the received serial number matches our serial number
// then send a response with the serial number. The domain address is set to 0 for closed media.
// An open medium BAU has to override this method and provide a proper domain address.
if (!memcmp(knxSerialNumber, _deviceObj.propertyData(PID_SERIAL_NUMBER), 6))
{
uint8_t emptyDomainAddress[2] = {0x00};
applicationLayer().IndividualAddressSerialNumberReadResponse(priority, hopType, secCtrl, emptyDomainAddress, knxSerialNumber);
}
}
void BauSystemB::addSaveRestore(SaveRestore* obj)
{
_memory.addSaveRestore(obj);
}
bool BauSystemB::restartRequest(uint16_t asap, const SecurityControl secCtrl)
{
if (applicationLayer().isConnected())
return false;
_restartState = Connecting; // order important, has to be set BEFORE connectRequest
_restartSecurity = secCtrl;
applicationLayer().connectRequest(asap, SystemPriority);
applicationLayer().deviceDescriptorReadRequest(AckRequested, SystemPriority, NetworkLayerParameter, asap, secCtrl, 0);
return true;
}
void BauSystemB::connectConfirm(uint16_t tsap)
{
if (_restartState == Connecting)
{
/* restart connection is confirmed, go to the next state */
_restartState = Connected;
_restartDelay = millis();
}
else
{
_restartState = Idle;
}
}
void BauSystemB::nextRestartState()
{
switch (_restartState)
{
case Idle:
/* inactive state, do nothing */
break;
case Connecting:
/* wait for connection, we do nothing here */
break;
case Connected:
/* connection confirmed, we send restartRequest, but we wait a moment (sending ACK etc)... */
if (millis() - _restartDelay > 30)
{
applicationLayer().restartRequest(AckRequested, SystemPriority, NetworkLayerParameter, _restartSecurity);
_restartState = Restarted;
_restartDelay = millis();
}
break;
case Restarted:
/* restart is finished, we send a disconnect */
if (millis() - _restartDelay > 30)
{
applicationLayer().disconnectRequest(SystemPriority);
_restartState = Idle;
}
default:
break;
}
}
void BauSystemB::systemNetworkParameterReadIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint16_t objectType,
uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength)
{
uint8_t operand;
popByte(operand, testInfo + 1); // First byte (+ 0) contains only 4 reserved bits (0)
// See KNX spec. 3.5.2 p.33 (Management Procedures: Procedures with A_SystemNetworkParameter_Read)
switch ((NmReadSerialNumberType)operand)
{
case NM_Read_SerialNumber_By_ProgrammingMode: // NM_Read_SerialNumber_By_ProgrammingMode
// Only send a reply if programming mode is on
if (_deviceObj.progMode() && (objectType == OT_DEVICE) && (propertyId == PID_SERIAL_NUMBER))
{
// Send reply. testResult data is KNX serial number
applicationLayer().systemNetworkParameterReadResponse(priority, hopType, secCtrl, objectType, propertyId,
testInfo, testInfoLength, (uint8_t*)_deviceObj.propertyData(PID_SERIAL_NUMBER), 6);
}
break;
case NM_Read_SerialNumber_By_ExFactoryState: // NM_Read_SerialNumber_By_ExFactoryState
break;
case NM_Read_SerialNumber_By_PowerReset: // NM_Read_SerialNumber_By_PowerReset
break;
case NM_Read_SerialNumber_By_ManufacturerSpecific: // Manufacturer specific use of A_SystemNetworkParameter_Read
break;
}
}
void BauSystemB::systemNetworkParameterReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint16_t objectType,
uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength, bool status)
{
}
void BauSystemB::propertyValueRead(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId,
uint8_t& numberOfElements, uint16_t startIndex,
uint8_t** data, uint32_t& length)
{
uint32_t size = 0;
uint8_t elementCount = numberOfElements;
InterfaceObject* obj = getInterfaceObject(objectType, objectInstance);
if (obj)
{
uint8_t elementSize = obj->propertySize((PropertyID)propertyId);
if (startIndex > 0)
size = elementSize * numberOfElements;
else
size = sizeof(uint16_t); // size of property array entry 0 which contains the current number of elements
*data = new uint8_t [size];
obj->readProperty((PropertyID)propertyId, startIndex, elementCount, *data);
}
else
{
elementCount = 0;
*data = nullptr;
}
numberOfElements = elementCount;
length = size;
}
void BauSystemB::propertyValueWrite(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId,
uint8_t& numberOfElements, uint16_t startIndex,
uint8_t* data, uint32_t length)
{
InterfaceObject* obj = getInterfaceObject(objectType, objectInstance);
if (obj)
obj->writeProperty((PropertyID)propertyId, startIndex, data, numberOfElements);
else
numberOfElements = 0;
}
Memory& BauSystemB::memory()
{
return _memory;
}
void BauSystemB::versionCheckCallback(VersionCheckCallback func)
{
_memory.versionCheckCallback(func);
}
VersionCheckCallback BauSystemB::versionCheckCallback()
{
return _memory.versionCheckCallback();
}
void BauSystemB::beforeRestartCallback(BeforeRestartCallback func)
{
_beforeRestart = func;
}
BeforeRestartCallback BauSystemB::beforeRestartCallback()
{
return _beforeRestart;
}
void BauSystemB::functionPropertyCallback(FunctionPropertyCallback func)
{
_functionProperty = func;
}
FunctionPropertyCallback BauSystemB::functionPropertyCallback()
{
return _functionProperty;
}
void BauSystemB::functionPropertyStateCallback(FunctionPropertyCallback func)
{
_functionPropertyState = func;
}
FunctionPropertyCallback BauSystemB::functionPropertyStateCallback()
{
return _functionPropertyState;
}

View File

@ -0,0 +1,133 @@
#pragma once
#include "config.h"
#include "bau.h"
#include "security_interface_object.h"
#include "application_program_object.h"
#include "application_layer.h"
#include "secure_application_layer.h"
#include "transport_layer.h"
#include "network_layer.h"
#include "data_link_layer.h"
#include "platform.h"
#include "memory.h"
class BauSystemB : protected BusAccessUnit
{
public:
BauSystemB(Platform& platform);
virtual void loop() = 0;
virtual bool configured() = 0;
virtual bool enabled() = 0;
virtual void enabled(bool value) = 0;
Platform& platform();
ApplicationProgramObject& parameters();
DeviceObject& deviceObject();
Memory& memory();
void readMemory();
void writeMemory();
void addSaveRestore(SaveRestore* obj);
bool restartRequest(uint16_t asap, const SecurityControl secCtrl);
uint8_t checkmasterResetValidity(EraseCode eraseCode, uint8_t channel);
void propertyValueRead(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId,
uint8_t& numberOfElements, uint16_t startIndex,
uint8_t** data, uint32_t& length) override;
void propertyValueWrite(ObjectType objectType, uint8_t objectInstance, uint8_t propertyId,
uint8_t& numberOfElements, uint16_t startIndex,
uint8_t* data, uint32_t length) override;
void versionCheckCallback(VersionCheckCallback func);
VersionCheckCallback versionCheckCallback();
void beforeRestartCallback(BeforeRestartCallback func);
BeforeRestartCallback beforeRestartCallback();
void functionPropertyCallback(FunctionPropertyCallback func);
FunctionPropertyCallback functionPropertyCallback();
void functionPropertyStateCallback(FunctionPropertyCallback func);
FunctionPropertyCallback functionPropertyStateCallback();
protected:
virtual ApplicationLayer& applicationLayer() = 0;
virtual InterfaceObject* getInterfaceObject(uint8_t idx) = 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;
void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
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,
uint32_t memoryAddress) override;
void deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t descriptorType) override;
void restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, RestartType restartType, EraseCode eraseCode, uint8_t channel) override;
void authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint32_t key) override;
void userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number, uint32_t memoryAddress) override;
void userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t number,
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,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length, bool confirmed);
void propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex) override;
void propertyValueExtReadIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ObjectType objectType, uint8_t objectInstance,
uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex) override;
void functionPropertyCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex,
uint8_t propertyId, uint8_t* data, uint8_t length) override;
void functionPropertyStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, uint8_t objectIndex,
uint8_t propertyId, uint8_t* data, uint8_t length) override;
void functionPropertyExtCommandIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ObjectType objectType, uint8_t objectInstance,
uint8_t propertyId, uint8_t* data, uint8_t length) override;
void functionPropertyExtStateIndication(Priority priority, HopCountType hopType, uint16_t asap, const SecurityControl& secCtrl, ObjectType objectType, uint8_t objectInstance,
uint8_t propertyId, uint8_t* data, uint8_t length) override;
void individualAddressReadIndication(HopCountType hopType, const SecurityControl& secCtrl) override;
void individualAddressWriteIndication(HopCountType hopType, const SecurityControl& secCtrl, uint16_t newaddress) override;
void individualAddressSerialNumberWriteIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint16_t newIndividualAddress,
uint8_t* knxSerialNumber) override;
void individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* knxSerialNumber) override;
void systemNetworkParameterReadIndication(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint16_t objectType,
uint16_t propertyId, uint8_t* testInfo, uint16_t testinfoLength) override;
void systemNetworkParameterReadLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint16_t objectType,
uint16_t propertyId, uint8_t* testInfo, uint16_t testInfoLength, bool status) override;
void connectConfirm(uint16_t tsap) override;
void nextRestartState();
virtual void doMasterReset(EraseCode eraseCode, uint8_t channel);
enum RestartState
{
Idle,
Connecting,
Connected,
Restarted
};
Memory _memory;
DeviceObject _deviceObj;
ApplicationProgramObject _appProgram;
Platform& _platform;
RestartState _restartState = Idle;
SecurityControl _restartSecurity;
uint32_t _restartDelay = 0;
BeforeRestartCallback _beforeRestart = 0;
FunctionPropertyCallback _functionProperty = 0;
FunctionPropertyCallback _functionPropertyState = 0;
};

View File

@ -0,0 +1,60 @@
#include "bau_systemB_coupler.h"
#include "bits.h"
#include <string.h>
#include <stdio.h>
BauSystemBCoupler::BauSystemBCoupler(Platform& platform) :
BauSystemB(platform),
_platform(platform),
#ifdef USE_DATASECURE
_appLayer(_deviceObj, _secIfObj, *this),
#else
_appLayer(*this),
#endif
_transLayer(_appLayer),
_netLayer(_deviceObj, _transLayer)
{
_appLayer.transportLayer(_transLayer);
_transLayer.networkLayer(_netLayer);
_memory.addSaveRestore(&_deviceObj);
#ifdef USE_DATASECURE
_memory.addSaveRestore(&_secIfObj);
#endif
}
ApplicationLayer& BauSystemBCoupler::applicationLayer()
{
return _appLayer;
}
void BauSystemBCoupler::loop()
{
_transLayer.loop();
#ifdef USE_DATASECURE
_appLayer.loop();
#endif
}
bool BauSystemBCoupler::configured()
{
// _configured is set to true initially, if the device was configured with ETS it will be set to true after restart
if (!_configured)
return false;
_configured = _appProgram.loadState() == LS_LOADED;
#ifdef USE_DATASECURE
_configured &= _secIfObj.loadState() == LS_LOADED;
#endif
return _configured;
}
void BauSystemBCoupler::doMasterReset(EraseCode eraseCode, uint8_t channel)
{
BauSystemB::doMasterReset(eraseCode, channel);
#ifdef USE_DATASECURE
_secIfObj.masterReset(eraseCode, channel);
#endif
}

View File

@ -0,0 +1,40 @@
#pragma once
#include "config.h"
#include "bau_systemB.h"
#include "device_object.h"
#include "security_interface_object.h"
#include "application_program_object.h"
#include "router_object.h"
#include "application_layer.h"
#include "secure_application_layer.h"
#include "transport_layer.h"
#include "network_layer_coupler.h"
#include "data_link_layer.h"
#include "platform.h"
#include "memory.h"
class BauSystemBCoupler : public BauSystemB
{
public:
BauSystemBCoupler(Platform& platform);
void loop() override;
bool configured() override;
protected:
ApplicationLayer& applicationLayer() override;
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
Platform& _platform;
#ifdef USE_DATASECURE
SecureApplicationLayer _appLayer;
SecurityInterfaceObject _secIfObj;
#else
ApplicationLayer _appLayer;
#endif
TransportLayer _transLayer;
NetworkLayerCoupler _netLayer;
bool _configured = true;
};

View File

@ -0,0 +1,262 @@
#include "bau_systemB_device.h"
#include "bits.h"
#include <string.h>
#include <stdio.h>
BauSystemBDevice::BauSystemBDevice(Platform& platform) :
BauSystemB(platform),
_addrTable(_memory),
_assocTable(_memory), _groupObjTable(_memory),
#ifdef USE_DATASECURE
_appLayer(_deviceObj, _secIfObj, *this),
#else
_appLayer(*this),
#endif
_transLayer(_appLayer), _netLayer(_deviceObj, _transLayer)
{
_appLayer.transportLayer(_transLayer);
_appLayer.associationTableObject(_assocTable);
#ifdef USE_DATASECURE
_appLayer.groupAddressTable(_addrTable);
#endif
_transLayer.networkLayer(_netLayer);
_transLayer.groupAddressTable(_addrTable);
_memory.addSaveRestore(&_deviceObj);
_memory.addSaveRestore(&_groupObjTable); // changed order for better memory management
_memory.addSaveRestore(&_addrTable);
_memory.addSaveRestore(&_assocTable);
#ifdef USE_DATASECURE
_memory.addSaveRestore(&_secIfObj);
#endif
}
ApplicationLayer& BauSystemBDevice::applicationLayer()
{
return _appLayer;
}
GroupObjectTableObject& BauSystemBDevice::groupObjectTable()
{
return _groupObjTable;
}
void BauSystemBDevice::loop()
{
_transLayer.loop();
sendNextGroupTelegram();
nextRestartState();
#ifdef USE_DATASECURE
_appLayer.loop();
#endif
_memory.loop();
}
void BauSystemBDevice::sendNextGroupTelegram()
{
if (!configured())
return;
static uint16_t startIdx = 1;
GroupObjectTableObject& table = _groupObjTable;
uint16_t objCount = table.entryCount();
for (uint16_t asap = startIdx; asap <= objCount; asap++)
{
GroupObject& go = table.get(asap);
ComFlag flag = go.commFlag();
if (flag != ReadRequest && flag != WriteRequest)
continue;
if (flag == WriteRequest)
{
#ifdef SMALL_GROUPOBJECT
GroupObject::processClassCallback(go);
#else
GroupObjectUpdatedHandler handler = go.callback();
if (handler)
handler(go);
#endif
}
if (!go.communicationEnable())
{
go.commFlag(Ok);
continue;
}
SecurityControl goSecurity;
goSecurity.toolAccess = false; // Secured group communication never uses the toolkey. ETS knows all keys, also the group keys.
#ifdef USE_DATASECURE
// Get security flags from Security Interface Object for this group object
goSecurity.dataSecurity = _secIfObj.getGroupObjectSecurity(asap);
#else
goSecurity.dataSecurity = DataSecurity::None;
#endif
if (flag == WriteRequest && go.transmitEnable())
{
uint8_t* data = go.valueRef();
_appLayer.groupValueWriteRequest(AckRequested, asap, go.priority(), NetworkLayerParameter, goSecurity, data,
go.sizeInTelegram());
}
else if (flag == ReadRequest)
{
_appLayer.groupValueReadRequest(AckRequested, asap, go.priority(), NetworkLayerParameter, goSecurity);
}
go.commFlag(Transmitting);
startIdx = asap + 1;
return;
}
startIdx = 1;
}
void BauSystemBDevice::updateGroupObject(GroupObject& go, uint8_t* data, uint8_t length)
{
uint8_t* goData = go.valueRef();
if (length != go.valueSize())
{
go.commFlag(Error);
return;
}
memcpy(goData, data, length);
if (go.commFlag() != WriteRequest)
{
go.commFlag(Updated);
#ifdef SMALL_GROUPOBJECT
GroupObject::processClassCallback(go);
#else
GroupObjectUpdatedHandler handler = go.callback();
if (handler)
handler(go);
#endif
}
else
{
go.commFlag(Updated);
}
}
bool BauSystemBDevice::configured()
{
// _configured is set to true initially, if the device was configured with ETS it will be set to true after restart
if (!_configured)
return false;
_configured = _groupObjTable.loadState() == LS_LOADED
&& _addrTable.loadState() == LS_LOADED
&& _assocTable.loadState() == LS_LOADED
&& _appProgram.loadState() == LS_LOADED;
#ifdef USE_DATASECURE
_configured &= _secIfObj.loadState() == LS_LOADED;
#endif
return _configured;
}
void BauSystemBDevice::doMasterReset(EraseCode eraseCode, uint8_t channel)
{
BauSystemB::doMasterReset(eraseCode, channel);
_addrTable.masterReset(eraseCode, channel);
_assocTable.masterReset(eraseCode, channel);
_groupObjTable.masterReset(eraseCode, channel);
#ifdef USE_DATASECURE
_secIfObj.masterReset(eraseCode, channel);
#endif
}
void BauSystemBDevice::groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* data, uint8_t dataLength, bool status)
{
GroupObject& go = _groupObjTable.get(asap);
if (status)
go.commFlag(Ok);
else
go.commFlag(Error);
}
void BauSystemBDevice::groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, bool status)
{
GroupObject& go = _groupObjTable.get(asap);
if (status)
go.commFlag(Ok);
else
go.commFlag(Error);
}
void BauSystemBDevice::groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl)
{
#ifdef USE_DATASECURE
DataSecurity requiredGoSecurity;
// Get security flags from Security Interface Object for this group object
requiredGoSecurity = _secIfObj.getGroupObjectSecurity(asap);
if (secCtrl.dataSecurity != requiredGoSecurity)
{
println("GroupValueRead: access denied due to wrong security flags");
return;
}
#endif
GroupObject& go = _groupObjTable.get(asap);
if (!go.communicationEnable() || !go.readEnable())
return;
uint8_t* data = go.valueRef();
_appLayer.groupValueReadResponse(AckRequested, asap, priority, hopType, secCtrl, data, go.sizeInTelegram());
}
void BauSystemBDevice::groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* data,
uint8_t dataLength)
{
GroupObject& go = _groupObjTable.get(asap);
if (!go.communicationEnable() || !go.responseUpdateEnable())
return;
updateGroupObject(go, data, dataLength);
}
void BauSystemBDevice::groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, uint8_t* data, uint8_t dataLength)
{
#ifdef USE_DATASECURE
DataSecurity requiredGoSecurity;
// Get security flags from Security Interface Object for this group object
requiredGoSecurity = _secIfObj.getGroupObjectSecurity(asap);
if (secCtrl.dataSecurity != requiredGoSecurity)
{
println("GroupValueWrite: access denied due to wrong security flags");
return;
}
#endif
GroupObject& go = _groupObjTable.get(asap);
if (!go.communicationEnable() || !go.writeEnable())
return;
updateGroupObject(go, data, dataLength);
}

View File

@ -0,0 +1,57 @@
#pragma once
#include "config.h"
#include "bau_systemB.h"
#include "device_object.h"
#include "address_table_object.h"
#include "association_table_object.h"
#include "group_object_table_object.h"
#include "security_interface_object.h"
#include "application_program_object.h"
#include "application_layer.h"
#include "secure_application_layer.h"
#include "transport_layer.h"
#include "network_layer_device.h"
#include "data_link_layer.h"
#include "platform.h"
#include "memory.h"
class BauSystemBDevice : public BauSystemB
{
public:
BauSystemBDevice(Platform& platform);
void loop() override;
bool configured() override;
GroupObjectTableObject& groupObjectTable();
protected:
ApplicationLayer& applicationLayer() override;
void groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl,
uint8_t* data, uint8_t dataLength, bool status) override;
void groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl, bool status) override;
void groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl) override;
void groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl,
uint8_t* data, uint8_t dataLength) override;
void groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, const SecurityControl& secCtrl,
uint8_t* data, uint8_t dataLength) override;
void sendNextGroupTelegram();
void updateGroupObject(GroupObject& go, uint8_t* data, uint8_t length);
void doMasterReset(EraseCode eraseCode, uint8_t channel) override;
AddressTableObject _addrTable;
AssociationTableObject _assocTable;
GroupObjectTableObject _groupObjTable;
#ifdef USE_DATASECURE
SecureApplicationLayer _appLayer;
SecurityInterfaceObject _secIfObj;
#else
ApplicationLayer _appLayer;
#endif
TransportLayer _transLayer;
NetworkLayerDevice _netLayer;
bool _configured = true;
};

View File

@ -0,0 +1,364 @@
#include "bits.h"
#include <cstring> // for memcpy()
const uint8_t* popByte(uint8_t& b, const uint8_t* data)
{
b = *data;
data += 1;
return data;
}
#ifndef KNX_NO_PRINT
void printHex(const char* suffix, const uint8_t* data, size_t length, bool newline)
{
print(suffix);
for (size_t i = 0; i < length; i++)
{
if (data[i] < 0x10)
{
print("0");
}
print(data[i], HEX);
print(" ");
}
if (newline)
{
println();
}
}
#endif
const uint8_t* popWord(uint16_t& w, const uint8_t* data)
{
w = getWord(data);
data += 2;
return data;
}
const uint8_t* popInt(uint32_t& i, const uint8_t* data)
{
i = getInt(data);
data += 4;
return data;
}
const uint8_t* popByteArray(uint8_t* dst, uint32_t size, const uint8_t* data)
{
for (uint32_t i = 0; i < size; i++)
dst[i] = data[i];
data += size;
return data;
}
uint8_t* pushByte(uint8_t b, uint8_t* data)
{
data[0] = b;
data += 1;
return data;
}
uint8_t* pushWord(uint16_t w, uint8_t* data)
{
data[0] = ((w >> 8) & 0xff);
data[1] = (w & 0xff);
data += 2;
return data;
}
uint8_t* pushInt(uint32_t i, uint8_t* data)
{
data[0] = ((i >> 24) & 0xff);
data[1] = ((i >> 16) & 0xff);
data[2] = ((i >> 8) & 0xff);
data[3] = (i & 0xff);
data += 4;
return data;
}
uint8_t* pushByteArray(const uint8_t* src, uint32_t size, uint8_t* data)
{
for (uint32_t i = 0; i < size; i++)
data[i] = src[i];
data += size;
return data;
}
uint16_t getWord(const uint8_t* data)
{
return (data[0] << 8) + data[1];
}
uint32_t getInt(const uint8_t* data)
{
return (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3];
}
void sixBytesFromUInt64(uint64_t num, uint8_t* toByteArray)
{
toByteArray[0] = ((num >> 40) & 0xff);
toByteArray[1] = ((num >> 32) & 0xff);
toByteArray[2] = ((num >> 24) & 0xff);
toByteArray[3] = ((num >> 16) & 0xff);
toByteArray[4] = ((num >> 8) & 0xff);
toByteArray[5] = (num & 0xff);
}
uint64_t sixBytesToUInt64(uint8_t* data)
{
uint64_t l = 0;
for (uint8_t i = 0; i < 6; i++)
{
l = (l << 8) + data[i];
}
return l;
}
// The CRC of the Memory Control Block Table Property is a CRC16-CCITT with the following
// parameters:
// Width = 16 bit
// Truncated polynomial = 1021h
// Initial value = FFFFh
// Input date is NOT reflected.
// Output CRC is NOT reflected.
// No XOR is performed on the output CRC.
// EXAMPLE The correct CRC16-CCITT of the string 123456789 is E5CCh.
uint16_t crc16Ccitt(uint8_t* input, uint16_t length)
{
uint32_t polynom = 0x1021;
uint32_t result = 0xffff;
for (uint32_t i = 0; i < 8 * ((uint32_t)length + 2); i++)
{
result <<= 1;
uint32_t nextBit;
nextBit = ((i / 8) < length) ? ((input[i / 8] >> (7 - (i % 8))) & 0x1) : 0;
result |= nextBit;
if ((result & 0x10000) != 0)
result ^= polynom;
}
return result & 0xffff;
}
uint16_t crc16Dnp(uint8_t* input, uint16_t length)
{
// CRC-16-DNP
// generator polynomial = 2^16 + 2^13 + 2^12 + 2^11 + 2^10 + 2^8 + 2^6 + 2^5 + 2^2 + 2^0
uint32_t pn = 0x13d65; // 1 0011 1101 0110 0101
// for much data, using a lookup table would be a way faster CRC calculation
uint32_t crc = 0;
for (uint32_t i = 0; i < length; i++)
{
uint8_t bite = input[i] & 0xff;
for (uint8_t b = 8; b -- > 0;)
{
bool bit = ((bite >> b) & 1) == 1;
bool one = (crc >> 15 & 1) == 1;
crc <<= 1;
if (one ^ bit)
crc ^= pn;
}
}
return (~crc) & 0xffff;
}
// Produce Arduino print and println in ESP IDF for ESP32 family using printf().
#ifndef ARDUINO
#ifdef ESP_PLATFORM
// Helper function to print a number in binary format
static void print_binary(unsigned long long n)
{
if (n == 0)
{
printf("0");
return;
}
// Buffer for the maximum possible bits in an unsigned long long
char binary_string[65];
int i = 0;
while (n > 0)
{
binary_string[i++] = (n % 2) + '0';
n /= 2;
}
binary_string[i] = '\0';
// Reverse the string to get the correct binary representation
for (int j = 0; j < i / 2; ++j)
{
char temp = binary_string[j];
binary_string[j] = binary_string[i - j - 1];
binary_string[i - j - 1] = temp;
}
printf("%s", binary_string);
}
// --- print function implementations ---
void print(const char str[]) {
printf("%s", str);
}
void print(char c) {
printf("%c", c);
}
void print(unsigned char b, int base) {
if (base == BIN) {
print_binary(b);
} else if (base == DEC) {
printf("%u", (unsigned int)b);
} else if (base == HEX) {
printf("%x", (unsigned int)b);
} else if (base == OCT) {
printf("%o", (unsigned int)b);
}
}
void print(int n, int base) {
if (base == BIN) {
print_binary(n);
} else if (base == DEC) {
printf("%d", n);
} else if (base == HEX) {
printf("%x", n);
} else if (base == OCT) {
printf("%o", n);
}
}
void print(unsigned int n, int base) {
if (base == BIN) {
print_binary(n);
} else if (base == DEC) {
printf("%u", n);
} else if (base == HEX) {
printf("%x", n);
} else if (base == OCT) {
printf("%o", n);
}
}
void print(long n, int base) {
if (base == BIN) {
print_binary(n);
} else if (base == DEC) {
printf("%ld", n);
} else if (base == HEX) {
printf("%lx", n);
} else if (base == OCT) {
printf("%lo", n);
}
}
void print(unsigned long n, int base) {
if (base == BIN) {
print_binary(n);
} else if (base == DEC) {
printf("%lu", n);
} else if (base == HEX) {
printf("%lx", n);
} else if (base == OCT) {
printf("%lo", n);
}
}
void print(long long n, int base) {
if (base == BIN) {
print_binary(n);
} else if (base == DEC) {
printf("%lld", n);
} else if (base == HEX) {
printf("%llx", n);
} else if (base == OCT) {
printf("%llo", n);
}
}
void print(unsigned long long n, int base) {
if (base == BIN) {
print_binary(n);
} else if (base == DEC) {
printf("%llu", n);
} else if (base == HEX) {
printf("%llx", n);
} else if (base == OCT) {
printf("%llo", n);
}
}
void print(double n) {
printf("%f", n);
}
void println(void) {
printf("\n");
}
void println(const char c[]) {
print(c);
println();
}
void println(char c) {
print(c);
println();
}
void println(unsigned char b, int base) {
print(b, base);
println();
}
void println(int num, int base) {
print(num, base);
println();
}
void println(unsigned int num, int base) {
print(num, base);
println();
}
void println(long num, int base) {
print(num, base);
println();
}
void println(unsigned long num, int base) {
print(num, base);
println();
}
void println(long long num, int base) {
print(num, base);
println();
}
void println(unsigned long long num, int base) {
print(num, base);
println();
}
void println(double num) {
print(num);
println();
}
#endif // ESP_PLATFORM
#endif // !ARDUINO

View File

@ -0,0 +1,161 @@
#pragma once
#include <cstddef>
#include <cstdint>
#if defined(__linux__)
#include <arpa/inet.h>
#elif defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_STM32) || defined (DeviceFamily_CC13X0)
#define getbyte(x,n) (*(((uint8_t*)&(x))+n))
#define htons(x) ( (getbyte(x,0)<<8) | getbyte(x,1) )
#define htonl(x) ( (getbyte(x,0)<<24) | (getbyte(x,1)<<16) | (getbyte(x,2)<<8) | getbyte(x,3) )
#define ntohs(x) htons(x)
#define ntohl(x) htonl(x)
#elif defined(LIBRETINY)
#include <lwip/udp.h>
#define htons(x) lwip_htons(x)
#define htonl(x) lwip_htonl(x)
#endif
#if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_STM32) || defined(LIBRETINY)
#include <Arduino.h>
#elif defined(ARDUINO_ARCH_ESP8266)
#include <Arduino.h>
#include <user_interface.h>
#elif defined(ARDUINO_ARCH_ESP32)
#include <Arduino.h>
#include <esp_wifi.h>
#elif defined(ESP_PLATFORM)
#include <lwip/inet.h>
#include <driver/gpio.h>
// // Define Arduino-like macros if needed for compatibility
#define lowByte(val) ((val)&255)
#define highByte(val) (((val) >> ((sizeof(val) - 1) << 3)) & 255)
#define bitRead(val, bitno) (((val) >> (bitno)) & 1)
#define DEC 10
#define HEX 16
#define OCT 8
#define BIN 2
#define LOW 0
#define HIGH 1
#define CHANGE GPIO_INTR_ANYEDGE
#define FALLING GPIO_INTR_NEGEDGE
#define RISING GPIO_INTR_POSEDGE
// Implement or map Arduino-like functions if needed
uint32_t millis();
typedef void (*IsrFuncPtr)(void); // Arduino-style
typedef void (*EspIsrFuncPtr)(void*); // ESP-IDF-style
void attachInterrupt(uint32_t pin, IsrFuncPtr callback, uint32_t mode);
#else // Non-Arduino platforms
#define lowByte(val) ((val)&255)
#define highByte(val) (((val) >> ((sizeof(val) - 1) << 3)) & 255)
#define bitRead(val, bitno) (((val) >> (bitno)) & 1)
// print functions are implemented in the platform files
#define DEC 10
#define HEX 16
#define INPUT (0x0)
#define OUTPUT (0x1)
#define INPUT_PULLUP (0x2)
#define INPUT_PULLDOWN (0x3)
#define LOW (0x0)
#define HIGH (0x1)
#define CHANGE 2
#define FALLING 3
#define RISING 4
void delay(uint32_t millis);
void delayMicroseconds (unsigned int howLong);
uint32_t millis();
void pinMode(uint32_t dwPin, uint32_t dwMode);
void digitalWrite(uint32_t dwPin, uint32_t dwVal);
uint32_t digitalRead(uint32_t dwPin);
typedef void (*voidFuncPtr)(void);
void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode);
#endif
#ifndef MIN
#define MIN(a, b) ((a < b) ? (a) : (b))
#endif
#ifndef MAX
#define MAX(a, b) ((a > b) ? (a) : (b))
#endif
#ifndef ABS
#define ABS(x) ((x > 0) ? (x) : (-x))
#endif
#ifndef KNX_NO_PRINT
void print(const char[]);
void print(char);
void print(unsigned char, int = DEC);
void print(int, int = DEC);
void print(unsigned int, int = DEC);
void print(long, int = DEC);
void print(unsigned long, int = DEC);
void print(long long, int = DEC);
void print(unsigned long long, int = DEC);
void print(double);
void println(const char[]);
void println(char);
void println(unsigned char, int = DEC);
void println(int, int = DEC);
void println(unsigned int, int = DEC);
void println(long, int = DEC);
void println(unsigned long, int = DEC);
void println(long long, int = DEC);
void println(unsigned long long, int = DEC);
void println(double);
void println(void);
void printHex(const char* suffix, const uint8_t* data, size_t length, bool newline = true);
#else
#define print(...) do {} while(0)
#define println(...) do {} while(0)
#define printHex(...) do {} while(0)
#endif
#ifdef KNX_ACTIVITYCALLBACK
#define KNX_ACTIVITYCALLBACK_DIR 0x00
#define KNX_ACTIVITYCALLBACK_DIR_RECV 0x00
#define KNX_ACTIVITYCALLBACK_DIR_SEND 0x01
#define KNX_ACTIVITYCALLBACK_IPUNICAST 0x02
#define KNX_ACTIVITYCALLBACK_NET 0x04
#endif
const uint8_t* popByte(uint8_t& b, const uint8_t* data);
const uint8_t* popWord(uint16_t& w, const uint8_t* data);
const uint8_t* popInt(uint32_t& i, const uint8_t* data);
const uint8_t* popByteArray(uint8_t* dst, uint32_t size, const uint8_t* data);
uint8_t* pushByte(uint8_t b, uint8_t* data);
uint8_t* pushWord(uint16_t w, uint8_t* data);
uint8_t* pushInt(uint32_t i, uint8_t* data);
uint8_t* pushByteArray(const uint8_t* src, uint32_t size, uint8_t* data);
uint16_t getWord(const uint8_t* data);
uint32_t getInt(const uint8_t* data);
void sixBytesFromUInt64(uint64_t num, uint8_t* toByteArray);
uint64_t sixBytesToUInt64(uint8_t* data);
uint16_t crc16Ccitt(uint8_t* input, uint16_t length);
uint16_t crc16Dnp(uint8_t* input, uint16_t length);
enum ParameterFloatEncodings
{
Float_Enc_DPT9 = 0, // 2 Byte. See Chapter 3.7.2 section 3.10 (Datapoint Types 2-Octet Float Value)
Float_Enc_IEEE754Single = 1, // 4 Byte. C++ float
Float_Enc_IEEE754Double = 2, // 8 Byte. C++ double
};
#if defined(ARDUINO_ARCH_SAMD)
// temporary undef until framework-arduino-samd > 1.8.9 is released. See https://github.com/arduino/ArduinoCore-samd/pull/399 for a PR should will probably address this
#undef max
#undef min
// end of temporary undef
#endif

View File

@ -0,0 +1,39 @@
#pragma once
#include "property.h"
class InterfaceObject;
template <class T> class CallbackProperty : public Property
{
public:
CallbackProperty(T* io, PropertyID id, bool writeEnable, PropertyDataType type, uint16_t maxElements,
uint8_t access, uint8_t (*readCallback)(T*, uint16_t, uint8_t, uint8_t*),
uint8_t (*writeCallback)(T*, uint16_t, uint8_t, const uint8_t*))
: Property(id, writeEnable, type, maxElements, access),
_interfaceObject(io), _readCallback(readCallback), _writeCallback(writeCallback)
{}
CallbackProperty(T* io, PropertyID id, bool writeEnable, PropertyDataType type, uint16_t maxElements,
uint8_t access, uint8_t (*readCallback)(T*, uint16_t, uint8_t, uint8_t*))
: Property(id, writeEnable, type, maxElements, access), _interfaceObject(io), _readCallback(readCallback)
{}
uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override
{
if (count == 0 || _readCallback == nullptr || start > _maxElements || start + count > _maxElements + 1)
return 0;
return _readCallback(_interfaceObject, start, count, data);
}
uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override
{
if (count == 0 || start > _maxElements || start + count > _maxElements + 1 || _writeCallback == nullptr)
return 0;
return _writeCallback(_interfaceObject, start, count, data);
}
private:
T* _interfaceObject = nullptr;
uint8_t (*_readCallback)(T*, uint16_t, uint8_t, uint8_t*) = nullptr;
uint8_t (*_writeCallback)(T*, uint16_t, uint8_t, const uint8_t*) = nullptr;
};

View File

@ -0,0 +1,403 @@
#include "cemi_frame.h"
#include "bits.h"
#include "string.h"
#include <stdio.h>
/*
cEMI Frame Format
+--------+--------+--------+--------+---------+---------+--------+---------+
| _data |
+--------+--------+--------+--------+---------+---------+--------+---------+
| LPDU |
+--------+--------+--------+--------+---------+---------+--------+---------+
| NPDU |
+---------+--------+--------+--------+--------+---------+---------+--------+---------+
| 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
Add.Info Length = 0x00 - no additional info
Control Field 1 =
Control Field 2 =
Source Address = 0x0000 - filled in by router/gateway with its source address which is
part of the KNX subnet
Dest. Address = KNX group or individual address (2 byte)
Data Length = Number of bytes of data in the APDU excluding the TPCI/APCI bits
APDU = Application Protocol Data Unit - the actual payload including transport
protocol control information (TPCI), application protocol control
information (APCI) and data passed as an argument from higher layers of
the KNX communication stack
Control Field 1
Bit |
------+---------------------------------------------------------------
7 | Frame Type - 0x0 for extended frame
| 0x1 for standard frame
------+---------------------------------------------------------------
6 | Reserved
|
------+---------------------------------------------------------------
5 | Repeat Flag - 0x0 repeat frame on medium in case of an error
| 0x1 do not repeat
------+---------------------------------------------------------------
4 | System Broadcast - 0x0 system broadcast
| 0x1 broadcast
------+---------------------------------------------------------------
3 | Priority - 0x0 system
| 0x1 normal
------+ 0x2 urgent
2 | 0x3 low
|
------+---------------------------------------------------------------
1 | Acknowledge Request - 0x0 no ACK requested
| (L_Data.req) 0x1 ACK requested
------+---------------------------------------------------------------
0 | Confirm - 0x0 no error
| (L_Data.con) - 0x1 error
------+---------------------------------------------------------------
Control Field 2
Bit |
------+---------------------------------------------------------------
7 | Destination Address Type - 0x0 individual address
| - 0x1 group address
------+---------------------------------------------------------------
6-4 | Hop Count (0-7)
------+---------------------------------------------------------------
3-0 | Extended Frame Format - 0x0 standard frame
------+---------------------------------------------------------------
*/
CemiFrame::CemiFrame(uint8_t* data, uint16_t length)
: _npdu(data + data[1] + NPDU_LPDU_DIFF, *this),
_tpdu(data + data[1] + TPDU_LPDU_DIFF, *this),
_apdu(data + data[1] + APDU_LPDU_DIFF, *this)
{
_data = data;
_ctrl1 = data + data[1] + CEMI_HEADER_SIZE;
_length = length;
}
CemiFrame::CemiFrame(uint8_t apduLength)
: _data(buffer),
_npdu(_data + NPDU_LPDU_DIFF, *this),
_tpdu(_data + TPDU_LPDU_DIFF, *this),
_apdu(_data + APDU_LPDU_DIFF, *this)
{
_ctrl1 = _data + CEMI_HEADER_SIZE;
memset(_data, 0, apduLength + APDU_LPDU_DIFF);
_ctrl1[0] |= Broadcast;
_npdu.octetCount(apduLength);
_length = _npdu.length() + NPDU_LPDU_DIFF;
}
CemiFrame::CemiFrame(const CemiFrame& other)
: _data(buffer),
_npdu(_data + NPDU_LPDU_DIFF, *this),
_tpdu(_data + TPDU_LPDU_DIFF, *this),
_apdu(_data + APDU_LPDU_DIFF, *this)
{
_ctrl1 = _data + CEMI_HEADER_SIZE;
_length = other._length;
memcpy(_data, other._data, other.totalLenght());
}
CemiFrame& CemiFrame::operator=(CemiFrame other)
{
_length = other._length;
_data = buffer;
_ctrl1 = _data + CEMI_HEADER_SIZE;
memcpy(_data, other._data, other.totalLenght());
_npdu._data = _data + NPDU_LPDU_DIFF;
_tpdu._data = _data + TPDU_LPDU_DIFF;
_apdu._data = _data + APDU_LPDU_DIFF;
return *this;
}
MessageCode CemiFrame::messageCode() const
{
return (MessageCode)_data[0];
}
void CemiFrame::messageCode(MessageCode msgCode)
{
_data[0] = msgCode;
}
uint16_t CemiFrame::totalLenght() const
{
return _length;
}
uint16_t CemiFrame::telegramLengthtTP() const
{
if (frameType() == StandardFrame)
return totalLenght() - 2; /*-AddInfo -MsgCode - only one CTRL + CRC, */
else
return totalLenght() - 1; /*-AddInfo -MsgCode + CRC, */
}
void CemiFrame::fillTelegramTP(uint8_t* data)
{
uint16_t len = telegramLengthtTP();
if (frameType() == StandardFrame)
{
uint8_t octet5 = (_ctrl1[1] & 0xF0) | (_ctrl1[6] & 0x0F);
data[0] = _ctrl1[0]; //CTRL
memcpy(data + 1, _ctrl1 + 2, 4); // SA, DA
data[5] = octet5; // LEN; Hopcount, ..
memcpy(data + 6, _ctrl1 + 7, len - 7); // APDU
}
else
{
memcpy(data, _ctrl1, len - 1);
}
data[len - 1] = calcCrcTP(data, len - 1);
}
#ifdef USE_RF
uint16_t CemiFrame::telegramLengthtRF() const
{
return totalLenght() - 3;
}
void CemiFrame::fillTelegramRF(uint8_t* data)
{
uint16_t len = telegramLengthtRF();
// We prepare the actual KNX telegram for RF here only.
// The packaging into blocks with CRC16 (Format based on FT3 Data Link Layer (IEC 870-5))
// is done in the RF Data Link Layer code.
// RF always uses the Extended Frame Format. However, the length field is missing (right before the APDU)
// as there is already a length field at the beginning of the raw RF frame which is also used by the
// physical layer to control the HW packet engine of the transceiver.
data[0] = _ctrl1[1] & 0x0F; // KNX CTRL field for RF (bits 3..0 EFF only), bits 7..4 are set to 0 for asynchronous RF frames
memcpy(data + 1, _ctrl1 + 2, 4); // SA, DA
data[5] = (_ctrl1[1] & 0xF0) | ((_rfLfn & 0x7) << 1) | ((_ctrl1[0] & 0x10) >> 4); // L/NPCI field: AT, Hopcount, LFN, AET
memcpy(data + 6, _ctrl1 + 7, len - 6); // APDU
//printHex("cEMI_fill: ", &data[0], len);
}
#endif
uint8_t* CemiFrame::data()
{
return _data;
}
uint16_t CemiFrame::dataLength()
{
return _length;
}
uint8_t CemiFrame::calcCrcTP(uint8_t* buffer, uint16_t len)
{
uint8_t crc = 0xFF;
for (uint16_t i = 0; i < len; i++)
crc ^= buffer[i];
return crc;
}
FrameFormat CemiFrame::frameType() const
{
return (FrameFormat)(_ctrl1[0] & StandardFrame);
}
void CemiFrame::frameType(FrameFormat type)
{
_ctrl1[0] &= ~StandardFrame;
_ctrl1[0] |= type;
}
Repetition CemiFrame::repetition() const
{
return (Repetition)(_ctrl1[0] & RepetitionAllowed);
}
void CemiFrame::repetition(Repetition rep)
{
_ctrl1[0] &= ~RepetitionAllowed;
_ctrl1[0] |= rep;
}
SystemBroadcast CemiFrame::systemBroadcast() const
{
return (SystemBroadcast)(_ctrl1[0] & Broadcast);
}
void CemiFrame::systemBroadcast(SystemBroadcast value)
{
_ctrl1[0] &= ~Broadcast;
_ctrl1[0] |= value;
}
Priority CemiFrame::priority() const
{
return (Priority)(_ctrl1[0] & LowPriority);
}
void CemiFrame::priority(Priority value)
{
_ctrl1[0] &= ~LowPriority;
_ctrl1[0] |= value;
}
AckType CemiFrame::ack() const
{
return (AckType)(_ctrl1[0] & AckRequested);
}
void CemiFrame::ack(AckType value)
{
_ctrl1[0] &= ~AckRequested;
_ctrl1[0] |= value;
}
Confirm CemiFrame::confirm() const
{
return (Confirm)(_ctrl1[0] & ConfirmError);
}
void CemiFrame::confirm(Confirm value)
{
_ctrl1[0] &= ~ConfirmError;
_ctrl1[0] |= value;
}
AddressType CemiFrame::addressType() const
{
return (AddressType)(_ctrl1[1] & GroupAddress);
}
void CemiFrame::addressType(AddressType value)
{
_ctrl1[1] &= ~GroupAddress;
_ctrl1[1] |= value;
}
uint8_t CemiFrame::hopCount() const
{
return ((_ctrl1[1] >> 4) & 0x7);
}
void CemiFrame::hopCount(uint8_t value)
{
_ctrl1[1] &= ~(0x7 << 4);
_ctrl1[1] |= ((value & 0x7) << 4);
}
uint16_t CemiFrame::sourceAddress() const
{
uint16_t addr;
popWord(addr, _ctrl1 + 2);
return addr;
}
void CemiFrame::sourceAddress(uint16_t value)
{
pushWord(value, _ctrl1 + 2);
}
uint16_t CemiFrame::destinationAddress() const
{
uint16_t addr;
popWord(addr, _ctrl1 + 4);
return addr;
}
void CemiFrame::destinationAddress(uint16_t value)
{
pushWord(value, _ctrl1 + 4);
}
#ifdef USE_RF
uint8_t* CemiFrame::rfSerialOrDoA() const
{
return _rfSerialOrDoA;
}
void CemiFrame::rfSerialOrDoA(const uint8_t* rfSerialOrDoA)
{
_rfSerialOrDoA = (uint8_t*)rfSerialOrDoA;
}
uint8_t CemiFrame::rfInfo() const
{
return _rfInfo;
}
void CemiFrame::rfInfo(uint8_t rfInfo)
{
_rfInfo = rfInfo;
}
uint8_t CemiFrame::rfLfn() const
{
return _rfLfn;
}
void CemiFrame::rfLfn(uint8_t rfLfn)
{
_rfLfn = rfLfn;
}
#endif
NPDU& CemiFrame::npdu()
{
return _npdu;
}
TPDU& CemiFrame::tpdu()
{
return _tpdu;
}
APDU& CemiFrame::apdu()
{
return _apdu;
}
bool CemiFrame::valid() const
{
uint8_t addInfoLen = _data[1];
uint8_t apduLen = _data[_data[1] + NPDU_LPDU_DIFF];
if (_length != 0 && _length != (addInfoLen + apduLen + NPDU_LPDU_DIFF + 2))
{
print("length issue, length: ");
print(_length);
print(" addInfoLen: ");
print(addInfoLen);
print(" apduLen: ");
print(apduLen);
print(" expected length: ");
println(addInfoLen + apduLen + NPDU_LPDU_DIFF + 2);
printHex("Frame: ", _data, _length, true);
return false;
}
if ((_ctrl1[0] & 0x40) > 0 // Bit 6 has do be 0
|| (_ctrl1[1] & 0xF) > 0 // only standard or extended frames
|| _npdu.octetCount() == 0xFF // not allowed
|| (_npdu.octetCount() > 15 && frameType() == StandardFrame)
)
{
print("Other issue");
return false;
}
return true;
}

View File

@ -0,0 +1,94 @@
#pragma once
#include "knx_types.h"
#include "stdint.h"
#include "npdu.h"
#include "tpdu.h"
#include "apdu.h"
#include "config.h"
#define NPDU_LPDU_DIFF 8
#define TPDU_NPDU_DIFF 1
#define APDU_TPDU_DIFF 0
#define TPDU_LPDU_DIFF (TPDU_NPDU_DIFF + NPDU_LPDU_DIFF)
#define APDU_LPDU_DIFF (APDU_TPDU_DIFF + TPDU_NPDU_DIFF + NPDU_LPDU_DIFF)
// Mesg Code and additional info length
#define CEMI_HEADER_SIZE 2
class CemiFrame
{
friend class DataLinkLayer;
public:
CemiFrame(uint8_t* data, uint16_t length);
CemiFrame(uint8_t apduLength);
CemiFrame(const CemiFrame& other);
CemiFrame& operator=(CemiFrame other);
MessageCode messageCode() const;
void messageCode(MessageCode value);
uint16_t totalLenght() const;
uint16_t telegramLengthtTP() const;
void fillTelegramTP(uint8_t* data);
uint16_t telegramLengthtRF() const;
void fillTelegramRF(uint8_t* data);
uint8_t* data();
uint16_t dataLength();
FrameFormat frameType() const;
void frameType(FrameFormat value);
Repetition repetition() const;
void repetition(Repetition value);
SystemBroadcast systemBroadcast() const;
void systemBroadcast(SystemBroadcast value);
Priority priority() const;
void priority(Priority value);
AckType ack() const;
void ack(AckType value);
Confirm confirm() const;
void confirm(Confirm value);
AddressType addressType() const;
void addressType(AddressType value);
uint8_t hopCount() const;
void hopCount(uint8_t value);
uint16_t sourceAddress() const;
void sourceAddress(uint16_t value);
uint16_t destinationAddress() const;
void destinationAddress(uint16_t value);
#ifdef USE_RF
// only for RF medium
uint8_t* rfSerialOrDoA() const;
void rfSerialOrDoA(const uint8_t* rfSerialOrDoA);
uint8_t rfInfo() const;
void rfInfo(uint8_t rfInfo);
uint8_t rfLfn() const;
void rfLfn(uint8_t rfInfo);
#endif
NPDU& npdu();
TPDU& tpdu();
APDU& apdu();
uint8_t calcCrcTP(uint8_t* buffer, uint16_t len);
bool valid() const;
private:
uint8_t buffer[0xff + NPDU_LPDU_DIFF] = {0}; //only valid of add info is zero
uint8_t* _data = 0;
uint8_t* _ctrl1 = 0;
NPDU _npdu;
TPDU _tpdu;
APDU _apdu;
uint16_t _length = 0; // only set if created from byte array
#ifdef USE_RF
// FIXME: integrate this propery in _data
// only for RF medium
uint8_t* _rfSerialOrDoA = 0;
uint8_t _rfInfo = 0;
uint8_t _rfLfn = 0xFF; // RF Data Link layer frame number
#endif
uint8_t _sourceInterfaceIndex;
};

View File

@ -0,0 +1,443 @@
#include "config.h"
#ifdef USE_CEMI_SERVER
#include "cemi_server.h"
#include "cemi_frame.h"
#include "bau_systemB.h"
#include "usb_tunnel_interface.h"
#include "data_link_layer.h"
#include "string.h"
#include "bits.h"
#include <stdio.h>
CemiServer::CemiServer(BauSystemB& bau)
: _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.
_clientAddress = _bau.deviceObject().individualAddress() + 1;
}
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;
}
void CemiServer::clientAddress(uint16_t value)
{
_clientAddress = value;
}
void CemiServer::dataConfirmationToTunnel(CemiFrame& frame)
{
MessageCode backupMsgCode = frame.messageCode();
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);
}
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
data[1] = 0x0A; // Total additional info length
data[2] = 0x02; // RF add. info: type
data[3] = 0x08; // RF add. info: length
data[4] = frame.rfInfo(); // RF add. info: info field (batt ok, bidir)
pushByteArray(frame.rfSerialOrDoA(), 6, &data[5]); // RF add. info:Serial or Domain Address
data[11] = frame.rfLfn(); // RF add. info: link layer frame number
memcpy(&data[12], &((frame.data())[2]), frame.dataLength() - 2);
}
else
{
#endif
memcpy(&data[0], frame.data(), frame.dataLength());
#ifdef USE_RF
}
#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)
{
switch (frame.messageCode())
{
case L_data_req:
{
handleLData(frame);
break;
}
case M_PropRead_req:
{
handleMPropRead(frame);
break;
}
case M_PropWrite_req:
{
handleMPropWrite(frame);
break;
}
case M_FuncPropCommand_req:
{
println("M_FuncPropCommand_req not implemented");
break;
}
case M_FuncPropStateRead_req:
{
println("M_FuncPropStateRead_req not implemented");
break;
}
case M_Reset_req:
{
handleMReset(frame);
break;
}
// we should never receive these: server -> client
case L_data_con:
case L_data_ind:
case M_PropInfo_ind:
case M_PropRead_con:
case M_PropWrite_con:
case M_FuncPropCommand_con:
//case M_FuncPropStateRead_con: // same value as M_FuncPropCommand_con
case M_Reset_ind:
default:
break;
}
}
void CemiServer::handleLData(CemiFrame& frame)
{
// Fill in the cEMI client address if the client sets
// source address to 0.
#ifndef KNX_TUNNELING
//We already set the correct IA
if (frame.sourceAddress() == 0x0000)
{
frame.sourceAddress(_clientAddress);
}
#endif
#ifdef USE_RF
if (_dataLinkLayer->mediumType() == DptMedium::KNX_RF)
{
// Check if we have additional info for RF
if (((frame.data())[1] == 0x0A) && // Additional info total length: we only handle one additional info of type RF
((frame.data())[2] == 0x02) && // Additional info type: RF
((frame.data())[3] == 0x08) ) // Additional info length of type RF: 8 bytes (fixed)
{
frame.rfInfo((frame.data())[4]);
// Use the values provided in the RF additonal info
if ( ((frame.data())[5] != 0x00) || ((frame.data())[6] != 0x00) || ((frame.data())[7] != 0x00) ||
((frame.data())[8] != 0x00) || ((frame.data())[9] != 0x00) || ((frame.data())[10] != 0x00) )
{
frame.rfSerialOrDoA(&((frame.data())[5]));
} // else leave the nullptr as it is
frame.rfLfn((frame.data())[11]);
}
// If the cEMI client does not provide a link layer frame number (LFN),
// we use our own counter.
// Note: There is another link layer frame number counter inside the RF data link layer class!
// That counter is solely for the local application!
// If we set a LFN here, the data link layer counter is NOT used!
if (frame.rfLfn() == 0xFF)
{
// Set Data Link Layer Frame Number
frame.rfLfn(_frameNumber);
// Link Layer frame number counts 0..7
_frameNumber = (_frameNumber + 1) & 0x7;
}
}
#endif
#ifdef KNX_LOG_TUNNELING
print("L_data_req: src: ");
print(frame.sourceAddress(), HEX);
print(" dst: ");
print(frame.destinationAddress(), HEX);
printHex(" frame: ", frame.data(), frame.dataLength());
#endif
_dataLinkLayer->dataRequestFromTunnel(frame);
}
void CemiServer::handleMPropRead(CemiFrame& frame)
{
#ifdef KNX_LOG_TUNNELING
print("M_PropRead_req: ");
#endif
uint16_t objectType;
popWord(objectType, &frame.data()[1]);
uint8_t objectInstance = frame.data()[3];
uint8_t propertyId = frame.data()[4];
uint8_t numberOfElements = frame.data()[5] >> 4;
uint16_t startIndex = frame.data()[6] | ((frame.data()[5] & 0x0F) << 8);
uint8_t* data = nullptr;
uint32_t dataSize = 0;
#ifdef KNX_LOG_TUNNELING
print("ObjType: ");
print(objectType, DEC);
print(" ObjInst: ");
print(objectInstance, DEC);
print(" PropId: ");
print(propertyId, DEC);
print(" NoE: ");
print(numberOfElements, DEC);
print(" startIdx: ");
print(startIndex, DEC);
#endif
// propertyValueRead() allocates memory for the data! Needs to be deleted again!
_bau.propertyValueRead((ObjectType)objectType, objectInstance, propertyId, numberOfElements, startIndex, &data, dataSize);
// Patch result for device address in device object
// The cEMI server will hand out the device address + 1 to the cEMI client (e.g. ETS),
// so that the device and the cEMI client/server connection(tunnel) can operate simultaneously.
// KNX IP Interfaces which offer multiple simultaneous tunnel connections seem to operate the same way.
// Each tunnel has its own cEMI client address which is based on the main device address.
if (((ObjectType) objectType == OT_DEVICE) &&
(propertyId == PID_DEVICE_ADDR) &&
(numberOfElements == 1))
{
data[0] = (uint8_t) (_clientAddress & 0xFF);
}
else if (((ObjectType) objectType == OT_DEVICE) &&
(propertyId == PID_SUBNET_ADDR) &&
(numberOfElements == 1))
{
data[0] = (uint8_t) ((_clientAddress >> 8) & 0xFF);
}
if (data && dataSize && numberOfElements)
{
#ifdef KNX_LOG_TUNNELING
printHex(" <- data: ", data, dataSize);
#endif
// Prepare positive response
uint8_t responseData[7 + dataSize];
memcpy(responseData, frame.data(), 7);
memcpy(&responseData[7], data, dataSize);
CemiFrame responseFrame(responseData, sizeof(responseData));
responseFrame.messageCode(M_PropRead_con);
#ifdef USE_USB
_usbTunnelInterface.sendCemiFrame(responseFrame);
#elif defined(KNX_TUNNELING)
_dataLinkLayerPrimary->dataRequestToTunnel(responseFrame);
#endif
delete[] data;
}
else
{
// Prepare negative response
uint8_t responseData[7 + 1];
memcpy(responseData, frame.data(), sizeof(responseData));
responseData[7] = Void_DP; // Set cEMI error code
responseData[5] = 0; // Set Number of elements to zero
printHex(" <- error: ", &responseData[7], 1);
println("");
CemiFrame responseFrame(responseData, sizeof(responseData));
responseFrame.messageCode(M_PropRead_con);
#ifdef USE_USB
_usbTunnelInterface.sendCemiFrame(responseFrame);
#elif defined(KNX_TUNNELING)
_dataLinkLayerPrimary->dataRequestToTunnel(responseFrame);
#endif
}
}
void CemiServer::handleMPropWrite(CemiFrame& frame)
{
print("M_PropWrite_req: ");
uint16_t objectType;
popWord(objectType, &frame.data()[1]);
uint8_t objectInstance = frame.data()[3];
uint8_t propertyId = frame.data()[4];
uint8_t numberOfElements = frame.data()[5] >> 4;
uint16_t startIndex = frame.data()[6] | ((frame.data()[5] & 0x0F) << 8);
uint8_t* requestData = &frame.data()[7];
uint32_t requestDataSize = frame.dataLength() - 7;
print("ObjType: ");
print(objectType, DEC);
print(" ObjInst: ");
print(objectInstance, DEC);
print(" PropId: ");
print(propertyId, DEC);
print(" NoE: ");
print(numberOfElements, DEC);
print(" startIdx: ");
print(startIndex, DEC);
printHex(" -> data: ", requestData, requestDataSize);
// Patch request for device address in device object
if (((ObjectType) objectType == OT_DEVICE) &&
(propertyId == PID_DEVICE_ADDR) &&
(numberOfElements == 1))
{
// Temporarily store new cEMI client address in memory
// We also be sent back if the client requests it again
_clientAddress = (_clientAddress & 0xFF00) | requestData[0];
print("cEMI client address: ");
println(_clientAddress, HEX);
}
else if (((ObjectType) objectType == OT_DEVICE) &&
(propertyId == PID_SUBNET_ADDR) &&
(numberOfElements == 1))
{
// Temporarily store new cEMI client address in memory
// We also be sent back if the client requests it again
_clientAddress = (_clientAddress & 0x00FF) | (requestData[0] << 8);
print("cEMI client address: ");
println(_clientAddress, HEX);
}
else
{
_bau.propertyValueWrite((ObjectType)objectType, objectInstance, propertyId, numberOfElements, startIndex, requestData, requestDataSize);
}
if (numberOfElements)
{
// Prepare positive response
uint8_t responseData[7];
memcpy(responseData, frame.data(), sizeof(responseData));
println(" <- no error");
CemiFrame responseFrame(responseData, sizeof(responseData));
responseFrame.messageCode(M_PropWrite_con);
#ifdef USE_USB
_usbTunnelInterface.sendCemiFrame(responseFrame);
#elif defined(KNX_TUNNELING)
_dataLinkLayerPrimary->dataRequestToTunnel(responseFrame);
#endif
}
else
{
// Prepare negative response
uint8_t responseData[7 + 1];
memcpy(responseData, frame.data(), sizeof(responseData));
responseData[7] = Illegal_Command; // Set cEMI error code
responseData[5] = 0; // Set Number of elements to zero
printHex(" <- error: ", &responseData[7], 1);
println("");
CemiFrame responseFrame(responseData, sizeof(responseData));
responseFrame.messageCode(M_PropWrite_con);
#ifdef USE_USB
_usbTunnelInterface.sendCemiFrame(responseFrame);
#elif defined(KNX_TUNNELING)
_dataLinkLayerPrimary->dataRequestToTunnel(responseFrame);
#endif
}
}
void CemiServer::handleMReset(CemiFrame& frame)
{
println("M_Reset_req: sending M_Reset_ind");
// A real device reset does not work for USB or KNXNET/IP.
// Thus, M_Reset_ind is NOT mandatory for USB and KNXNET/IP.
// We just save all data to the EEPROM
_bau.writeMemory();
// Prepare response
uint8_t responseData[1];
CemiFrame responseFrame(responseData, sizeof(responseData));
responseFrame.messageCode(M_Reset_ind);
#ifdef USE_USB
_usbTunnelInterface.sendCemiFrame(responseFrame);
#elif defined(KNX_TUNNELING)
_dataLinkLayerPrimary->dataRequestToTunnel(responseFrame);
#endif
}
void CemiServer::loop()
{
#ifdef USE_USB
_usbTunnelInterface.loop();
#endif
}
#endif

View File

@ -0,0 +1,68 @@
#pragma once
#include "config.h"
#ifdef USE_CEMI_SERVER
#include <stdint.h>
#include "knx_types.h"
#include "usb_tunnel_interface.h"
class BauSystemB;
class DataLinkLayer;
class CemiFrame;
/**
* This is an implementation of the cEMI server as specified in @cite knx:3/6/3.
* Overview on page 57.
* It provides methods for the BusAccessUnit to do different things and translates this
* call to an cEMI frame and calls the correct method of the data link layer.
* It also takes calls from data link layer, decodes the submitted cEMI frames and calls the corresponding
* methods of the BusAccessUnit class.
*/
class CemiServer
{
public:
/**
* The constructor.
* @param bau methods are called here depending of the content of the APDU
*/
CemiServer(BauSystemB& bau);
void dataLinkLayer(DataLinkLayer& layer);
#ifdef KNX_TUNNELING
void dataLinkLayerPrimary(DataLinkLayer& layer);
#endif
// from data link layer
// Only L_Data service
void dataIndicationToTunnel(CemiFrame& frame);
void dataConfirmationToTunnel(CemiFrame& frame);
// From tunnel interface
void frameReceived(CemiFrame& frame);
uint16_t clientAddress() const;
void clientAddress(uint16_t value);
void loop();
private:
uint16_t _clientAddress = 0;
uint8_t _frameNumber = 0;
void handleLData(CemiFrame& frame);
void handleMPropRead(CemiFrame& frame);
void handleMPropWrite(CemiFrame& frame);
void handleMReset(CemiFrame& frame);
DataLinkLayer* _dataLinkLayer = nullptr;
#ifdef KNX_TUNNELING
DataLinkLayer* _dataLinkLayerPrimary = nullptr;
#endif
BauSystemB& _bau;
#ifdef USE_USB
UsbTunnelInterface _usbTunnelInterface;
#endif
};
#endif

View File

@ -0,0 +1,61 @@
#include "config.h"
#ifdef USE_CEMI_SERVER
#include <cstring>
#include "cemi_server_object.h"
#include "bits.h"
#include "data_property.h"
CemiServerObject::CemiServerObject()
{
Property* properties[] =
{
new DataProperty( PID_OBJECT_TYPE, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t)OT_CEMI_SERVER ),
new DataProperty( PID_MEDIUM_TYPE, false, PDT_BITSET16, 1, ReadLv3 | WriteLv0, (uint16_t)0),
new DataProperty( PID_COMM_MODE, false, PDT_ENUM8, 1, ReadLv3 | WriteLv0, (uint16_t)0),
new DataProperty( PID_COMM_MODES_SUPPORTED, false, PDT_BITSET16, 1, ReadLv3 | WriteLv0, (uint16_t)0x100),
new DataProperty( PID_MEDIUM_AVAILABILITY, false, PDT_BITSET16, 1, ReadLv3 | WriteLv0, (uint16_t)0),
// cEMI additional info types supported by this cEMI server: only 0x02 (RF Control Octet and Serial Number or DoA)
new DataProperty( PID_ADD_INFO_TYPES, false, PDT_ENUM8, 1, ReadLv3 | WriteLv0, (uint8_t)0x02)
};
initializeProperties(sizeof(properties), properties);
}
void CemiServerObject::setMediumTypeAsSupported(DptMedium dptMedium)
{
uint16_t mediaTypesSupported;
property(PID_MEDIUM_TYPE)->read(mediaTypesSupported);
switch (dptMedium)
{
case DptMedium::KNX_IP:
mediaTypesSupported |= 1 << 1;
break;
case DptMedium::KNX_RF:
mediaTypesSupported |= 1 << 4;
break;
case DptMedium::KNX_TP1:
mediaTypesSupported |= 1 << 5;
break;
case DptMedium::KNX_PL110:
mediaTypesSupported |= 1 << 2;
break;
}
property(PID_MEDIUM_TYPE)->write(mediaTypesSupported);
// We also set the medium as available too
property(PID_MEDIUM_AVAILABILITY)->write(mediaTypesSupported);
}
void CemiServerObject::clearSupportedMediaTypes()
{
property(PID_MEDIUM_TYPE)->write((uint16_t) 0);
// We also set the medium as not available too
property(PID_MEDIUM_AVAILABILITY)->write((uint16_t) 0);
}
#endif

View File

@ -0,0 +1,17 @@
#pragma once
#include "config.h"
#ifdef USE_CEMI_SERVER
#include "interface_object.h"
class CemiServerObject: public InterfaceObject
{
public:
CemiServerObject();
void setMediumTypeAsSupported(DptMedium dptMedium);
void clearSupportedMediaTypes();
};
#endif

View File

@ -0,0 +1,84 @@
#pragma once
#ifndef NO_KNX_CONFIG
#ifdef ARDUINO_ARCH_SAMD
#define SPI_SS_PIN 10
#define GPIO_GDO2_PIN 9
#define GPIO_GDO0_PIN 7
#else // Linux Platform (Raspberry Pi)
#define SPI_SS_PIN 8 // GPIO 8 (SPI_CE0_N) -> WiringPi: 10 -> Pin number on header: 24
#define GPIO_GDO2_PIN 25 // GPIO 25 (GPIO_GEN6) -> WiringPi: 6 -> Pin number on header: 22
#define GPIO_GDO0_PIN 24 // GPIO 24 (GPIO_GEN5) -> WiringPi: 5 -> Pin number on header: 18
#endif
// Normal devices
// TP1: 0x07B0
// RF: 0x27B0
// IP: 0x57B0
//#define MASK_VERSION 0x07B0
//#define MASK_VERSION 0x27B0
//#define MASK_VERSION 0x57B0
// Couplers
// IP/TP1: 0x091A
// TP1/RF: 0x2920
//#define MASK_VERSION 0x091A
//#define MASK_VERSION 0x2920
// Data Linklayer Driver Options
#if MASK_VERSION == 0x07B0
#define USE_TP
#endif
#if MASK_VERSION == 0x27B0
#define USE_RF
#endif
#if MASK_VERSION == 0x57B0
#define USE_IP
#endif
#if MASK_VERSION == 0x091A
#define USE_TP
#define USE_IP
#endif
#if MASK_VERSION == 0x2920
#define USE_TP
#define USE_RF
#endif
// cEMI options
//#define USE_USB
//#define USE_CEMI_SERVER
#if defined(USE_USB) || defined(KNX_TUNNELING)
#define USE_CEMI_SERVER
#endif
// KNX Data Secure Options
// Define via a compiler -D flag if required
// #define USE_DATASECURE
// option to have GroupObjects (KO in German) use 8 bytes mangement information RAM instead of 19 bytes
// see knx-demo-small-go for example
// this option might be also set via compiler flag -DSMALL_GROUPOBJECT if required
//#define SMALL_GROUPOBJECT
// Some defines to reduce footprint
// Do not perform conversion from KNXValue(const char*) to other types, it mainly avoids the expensive strtod
//#define KNX_NO_STRTOx_CONVERSION
// Do not print messages
//#define KNX_NO_PRINT
// Do not use SPI (Arduino variants)
//#define KNX_NO_SPI
// Do not use the default UART (Arduino variants), it must be defined by ArduinoPlatform::knxUart
// (combined with other flags (HWSERIAL_NONE for stm32) - avoid allocation of RX/TX buffers for all serial lines)
//#define KNX_NO_DEFAULT_UART
#endif
#if !defined(MASK_VERSION)
#error MASK_VERSION must be defined! See config.h for possible values!
#endif

View File

@ -0,0 +1,309 @@
#include "data_link_layer.h"
#include "bits.h"
#include "platform.h"
#include "device_object.h"
#include "cemi_server.h"
#include "cemi_frame.h"
void DataLinkLayerCallbacks::activity(uint8_t info)
{
if (_activityCallback)
_activityCallback(info);
}
void DataLinkLayerCallbacks::setActivityCallback(ActivityCallback activityCallback)
{
_activityCallback = activityCallback;
}
DataLinkLayer::DataLinkLayer(DeviceObject& devObj, NetworkLayerEntity& netLayerEntity, Platform& platform) :
_deviceObject(devObj), _networkLayerEntity(netLayerEntity), _platform(platform)
{
#ifdef KNX_ACTIVITYCALLBACK
_netIndex = netLayerEntity.getEntityIndex();
#endif
}
#ifdef USE_CEMI_SERVER
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 ( => 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);
}
#endif
void DataLinkLayer::dataRequest(AckType ack, AddressType addrType, uint16_t destinationAddr, uint16_t sourceAddr, FrameFormat format, Priority priority, NPDU& npdu)
{
// Normal data requests and broadcasts will always be transmitted as (domain) broadcast with domain address for open media (e.g. RF medium)
// The domain address "simulates" a closed medium (such as TP) on an open medium (such as RF or PL)
// See 3.2.5 p.22
sendTelegram(npdu, ack, destinationAddr, addrType, sourceAddr, format, priority, Broadcast);
}
void DataLinkLayer::systemBroadcastRequest(AckType ack, FrameFormat format, Priority priority, NPDU& npdu, uint16_t sourceAddr)
{
// System Broadcast requests will always be transmitted as broadcast with KNX serial number for open media (e.g. RF medium)
// See 3.2.5 p.22
sendTelegram(npdu, ack, 0, GroupAddress, sourceAddr, format, priority, SysBroadcast);
}
void DataLinkLayer::dataConReceived(CemiFrame& frame, bool success)
{
MessageCode backupMsgCode = frame.messageCode();
frame.messageCode(L_data_con);
frame.confirm(success ? ConfirmNoError : ConfirmError);
AckType ack = frame.ack();
AddressType addrType = frame.addressType();
uint16_t destination = frame.destinationAddress();
uint16_t source = frame.sourceAddress();
FrameFormat type = frame.frameType();
Priority priority = frame.priority();
NPDU& npdu = frame.npdu();
SystemBroadcast systemBroadcast = frame.systemBroadcast();
#ifdef USE_CEMI_SERVER
// if the confirmation was caused by a tunnel request then
// do not send it to the local stack
if (frame.sourceAddress() == _cemiServer->clientAddress())
{
// Stop processing here and do NOT send it the local network layer
return;
}
#endif
if (addrType == GroupAddress && destination == 0)
if (systemBroadcast == SysBroadcast)
_networkLayerEntity.systemBroadcastConfirm(ack, type, priority, source, npdu, success);
else
_networkLayerEntity.broadcastConfirm(ack, type, priority, source, npdu, success);
else
_networkLayerEntity.dataConfirm(ack, addrType, destination, type, priority, source, npdu, success);
frame.messageCode(backupMsgCode);
}
void DataLinkLayer::frameReceived(CemiFrame& frame)
{
AckType ack = frame.ack();
AddressType addrType = frame.addressType();
uint16_t destination = frame.destinationAddress();
uint16_t source = frame.sourceAddress();
FrameFormat type = frame.frameType();
Priority priority = frame.priority();
NPDU& npdu = frame.npdu();
uint16_t ownAddr = _deviceObject.individualAddress();
SystemBroadcast systemBroadcast = frame.systemBroadcast();
#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);
if (addrType == GroupAddress && destination == 0)
{
if (systemBroadcast == SysBroadcast)
_networkLayerEntity.systemBroadcastIndication(ack, type, npdu, priority, source);
else
_networkLayerEntity.broadcastIndication(ack, type, npdu, priority, source);
}
else
{
_networkLayerEntity.dataIndication(ack, addrType, destination, type, npdu, priority, source);
}
}
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(doNotRepeat ? NoRepitiion : RepetitionAllowed);
frame.systemBroadcast(systemBroadcast);
if (npdu.octetCount() <= 15)
frame.frameType(StandardFrame);
else
frame.frameType(format);
if (!frame.valid())
{
println("invalid frame");
return false;
}
// if (frame.npdu().octetCount() > 0)
// {
// _print("<- DLL ");
// 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!
if (sendTheFrame)
success = sendFrame(frame);
#ifdef USE_CEMI_SERVER
CemiFrame tmpFrame(frame.data(), frame.totalLenght());
// We can just copy the pointer for rfSerialOrDoA as sendFrame() sets
// a pointer to const uint8_t data in either device object (serial) or
// RF medium object (domain address)
#ifdef USE_RF
tmpFrame.rfSerialOrDoA(frame.rfSerialOrDoA());
tmpFrame.rfInfo(frame.rfInfo());
tmpFrame.rfLfn(frame.rfLfn());
#endif
tmpFrame.confirm(ConfirmNoError);
if (_networkLayerEntity.getEntityIndex() == 1) // only send to tunnel if we are the secondary (TP) interface
_cemiServer->dataIndicationToTunnel(tmpFrame);
#endif
return success;
}
uint8_t* DataLinkLayer::frameData(CemiFrame& frame)
{
return frame._data;
}
#ifdef KNX_TUNNELING
bool DataLinkLayer::isTunnelingPA(uint16_t pa)
{
uint8_t numAddresses = 0;
uint16_t* addresses = _ipParameters->additionalIndivualAddresses(numAddresses);
for (uint8_t i = 0; i < numAddresses; i++)
{
if (pa == addresses[i])
return true;
}
return false;
}
bool DataLinkLayer::isRoutedPA(uint16_t pa)
{
uint16_t ownpa = _deviceObject.individualAddress();
uint16_t own_sm;
if ((ownpa & 0x0F00) == 0x0)
own_sm = 0xF000;
else
own_sm = 0xFF00;
return (pa & own_sm) != ownpa;
}
#endif

View File

@ -0,0 +1,77 @@
#pragma once
#include "config.h"
#include <stdint.h>
#include "device_object.h"
#include "knx_types.h"
#include "network_layer_entity.h"
#ifdef KNX_TUNNELING
#include "ip_parameter_object.h"
#endif
#include "cemi_server.h"
#include "bau.h"
class Platform;
typedef void (*ActivityCallback)(uint8_t info);
class DataLinkLayerCallbacks
{
protected:
ActivityCallback _activityCallback = nullptr;
public:
virtual ~DataLinkLayerCallbacks() = default;
virtual void activity(uint8_t info);
virtual void setActivityCallback(ActivityCallback activityCallback);
};
class DataLinkLayer
{
public:
DataLinkLayer(DeviceObject& devObj, NetworkLayerEntity& netLayerEntity,
Platform& platform);
#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);
void ipParameterObject(IpParameterObject* object);
#endif
#endif
// from network layer
void dataRequest(AckType ack, AddressType addrType, uint16_t destinationAddr, uint16_t sourceAddr, FrameFormat format,
Priority priority, NPDU& npdu);
void systemBroadcastRequest(AckType ack, FrameFormat format, Priority priority, NPDU& npdu, uint16_t sourceAddr);
virtual void loop() = 0;
virtual void enabled(bool value) = 0;
virtual bool enabled() const = 0;
virtual DptMedium mediumType() const = 0;
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 doNotRepeat = false);
virtual bool sendFrame(CemiFrame& frame) = 0;
uint8_t* frameData(CemiFrame& frame);
DeviceObject& _deviceObject;
NetworkLayerEntity& _networkLayerEntity;
Platform& _platform;
#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);
IpParameterObject* _ipParameters;
#endif
};

View File

@ -0,0 +1,168 @@
#include "data_property.h"
#include "bits.h"
#include <cstring>
uint8_t DataProperty::read(uint16_t start, uint8_t count, uint8_t* data) const
{
if (start == 0)
{
pushWord(_currentElements, data);
return 1;
}
if (count == 0 || _currentElements == 0 || start > _currentElements || count > _currentElements - start + 1)
return 0;
// we start counting with zero
start -= 1;
// data is already big enough to hold the data
memcpy(data, _data + (start * ElementSize()), count * ElementSize());
return count;
}
uint8_t DataProperty::write(uint16_t start, uint8_t count, const uint8_t* data)
{
if (count == 0 || start > _maxElements || start + count > _maxElements + 1)
return 0;
if (start == 0)
{
if (count == 1 && data[0] == 0 && data[1] == 0)
{
// reset _data
_currentElements = 0;
if (_data)
{
delete[] _data;
_data = nullptr;
}
return 1;
}
else
return 0;
}
// we start counting with zero
start -= 1;
if (start + count > _currentElements)
{
// reallocate memory for _data
uint8_t* oldData = _data;
size_t oldDataSize = _currentElements * ElementSize();
size_t newDataSize = (start + count) * ElementSize();
_data = new uint8_t[newDataSize];
memset(_data, 0, newDataSize);
if (oldData != nullptr)
{
memcpy(_data, oldData, oldDataSize);
delete[] oldData;
}
_currentElements = start + count;
}
memcpy(_data + (start * ElementSize()), data, count * ElementSize());
return count;
}
DataProperty::DataProperty(PropertyID id, bool writeEnable, PropertyDataType type,
uint16_t maxElements, uint8_t access)
: Property(id, writeEnable, type, maxElements, access)
{}
DataProperty::~DataProperty()
{
if (_data)
delete[] _data;
}
DataProperty::DataProperty(PropertyID id, bool writeEnable, PropertyDataType type,
uint16_t maxElements, uint8_t access, uint16_t value)
: Property(id, writeEnable, type, maxElements, access)
{
Property::write(value);
}
DataProperty::DataProperty(PropertyID id, bool writeEnable, PropertyDataType type,
uint16_t maxElements, uint8_t access, uint32_t value)
: Property(id, writeEnable, type, maxElements, access)
{
Property::write(value);
}
DataProperty::DataProperty(PropertyID id, bool writeEnable, PropertyDataType type,
uint16_t maxElements, uint8_t access, uint8_t value)
: Property(id, writeEnable, type, maxElements, access)
{
Property::write(value);
}
DataProperty::DataProperty(PropertyID id, bool writeEnable, PropertyDataType type,
uint16_t maxElements, uint8_t access, const uint8_t* value)
: Property(id, writeEnable, type, maxElements, access)
{
Property::write(value);
}
uint16_t DataProperty::saveSize()
{
return sizeof(_currentElements) + _maxElements * ElementSize();
}
const uint8_t* DataProperty::restore(const uint8_t* buffer)
{
uint16_t elements = 0;
buffer = popWord(elements, buffer);
if (elements != _currentElements)
{
if (_data != nullptr)
delete[] _data;
_data = new uint8_t[elements * ElementSize()];
_currentElements = elements;
}
if (elements > 0)
buffer = popByteArray(_data, elements * ElementSize(), buffer);
return buffer;
}
uint8_t* DataProperty::save(uint8_t* buffer)
{
buffer = pushWord(_currentElements, buffer);
if (_currentElements > 0)
buffer = pushByteArray(_data, _currentElements * ElementSize(), buffer);
return buffer;
}
const uint8_t* DataProperty::data()
{
return _data;
}
const uint8_t* DataProperty::data(uint16_t elementIndex)
{
if ((elementIndex == 0) || (elementIndex > _currentElements))
return nullptr;
elementIndex -= 1; // Starting from 0
uint16_t offset = elementIndex * ElementSize();
return _data + offset;
}

View File

@ -0,0 +1,25 @@
#pragma once
#include "property.h"
class DataProperty : public Property
{
public:
DataProperty(PropertyID id, bool writeEnable, PropertyDataType type, uint16_t maxElements, uint8_t access);
DataProperty(PropertyID id, bool writeEnable, PropertyDataType type, uint16_t maxElements, uint8_t access, uint8_t value);
DataProperty(PropertyID id, bool writeEnable, PropertyDataType type, uint16_t maxElements, uint8_t access, uint16_t value);
DataProperty(PropertyID id, bool writeEnable, PropertyDataType type, uint16_t maxElements, uint8_t access, uint32_t value);
DataProperty(PropertyID id, bool writeEnable, PropertyDataType type, uint16_t maxElements, uint8_t access, const uint8_t* value);
~DataProperty() override;
uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override;
uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override;
uint8_t* save(uint8_t* buffer) override;
const uint8_t* restore(const uint8_t* buffer) override;
uint16_t saveSize() override;
const uint8_t* data();
const uint8_t* data(uint16_t elementIndex);
private:
uint16_t _currentElements = 0;
uint8_t* _data = nullptr;
};

View File

@ -0,0 +1,64 @@
/*
* datapoint_types.h - Conversion functions for datapoint types.
*
* Copyright (c) 2014 Stefan Taferner <stefan.taferner@gmx.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*/
#include "datapoint_types.h"
#include <stdint.h>
// Sign for a negative DPT9 float value
#define DPT_FLOAT_NEG_SIGN 0x8000
uint16_t dptToFloat(int32_t value)
{
uint16_t exp = 0;
if (value < -67108864 || value > 67076096)
return 0x7fff;
if (value < 0)
{
while (value < -2048)
{
value >>= 1;
++exp;
}
return DPT_FLOAT_NEG_SIGN | (((int32_t) value) & 2047) | (exp << 11);
}
else
{
while (value > 2047)
{
value >>= 1;
++exp;
}
return value | (exp << 11);
}
}
int32_t dptFromFloat(uint16_t dptValue)
{
uint16_t exp = (dptValue >> 11) & 15;
int32_t value;
if (dptValue == 0x7fff)
return INVALID_DPT_FLOAT;
if (dptValue >= 0x8000)
value = dptValue | (-1L & ~2047);
else
value = dptValue & 2047;
for (; exp; --exp)
value <<= 1;
return value;
}

View File

@ -0,0 +1,37 @@
/*
* datapoint_types.h - Conversion functions for datapoint types.
*
* Copyright (c) 2014 Stefan Taferner <stefan.taferner@gmx.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3 as
* published by the Free Software Foundation.
*/
#pragma once
#include <stdint.h>
/**
* An invalid 2 uint8_t float (DPT9/EIS5).
* To be used for dptToFloat() and dptFromFloat().
*/
#define INVALID_DPT_FLOAT 2147483647U
/**
* Convert a value from uint32_t to 2 uint8_t float (DPT9/EIS5). The possible range
* of the values is -67108864 to 67076096.
*
* @param value - the value to convert.
* Use INVALID_DPT_FLOAT for the DPT9 "invalid data" value.
* @return The 2 uint8_t float (DPT9/EIS5).
*/
uint16_t dptToFloat(int32_t value);
/**
* Convert a value from 2 uint8_t float (DPT9/EIS5) to integer.
*
* @param dptValue - the 2 uint8_t float (DPT9/EIS5) to convert
* @return The value as integer, or INVALID_DPT_FLOAT for the
* DPT9 "invalid data" value.
*/
int32_t dptFromFloat(uint16_t dptValue);

View File

@ -0,0 +1,294 @@
#include <cstring>
#include "device_object.h"
#include "bits.h"
#include "data_property.h"
#include "callback_property.h"
#include "config.h"
#define LEN_KNX_SERIAL 6
DeviceObject::DeviceObject()
{
// Default to KNXA (0xFA)
// Note: ETS does not accept a SN 00FA00000000 in data secure mode.
// ETS says that 00FA00000000 looks "suspicious" in the log file.
uint8_t serialNumber[] = {0x00, 0xFA, 0x01, 0x02, 0x03, 0x04};
uint8_t hardwareType[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
Property* properties[] =
{
new DataProperty(PID_OBJECT_TYPE, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t)OT_DEVICE),
new DataProperty(PID_SERIAL_NUMBER, false, PDT_GENERIC_06, 1, ReadLv3 | WriteLv0, serialNumber),
new CallbackProperty<DeviceObject>(this, PID_MANUFACTURER_ID, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0,
[](DeviceObject * io, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t
{
if (start == 0)
{
uint16_t currentNoOfElements = 1;
pushWord(currentNoOfElements, data);
return 1;
}
pushByteArray(io->propertyData(PID_SERIAL_NUMBER), 2, data);
return 1;
}),
new DataProperty(PID_DEVICE_CONTROL, true, PDT_BITSET8, 1, ReadLv3 | WriteLv3, (uint8_t)0),
new DataProperty(PID_ORDER_INFO, false, PDT_GENERIC_10, 1, ReadLv3 | WriteLv0),
new DataProperty(PID_VERSION, false, PDT_VERSION, 1, ReadLv3 | WriteLv0, (uint16_t)3),
new DataProperty(PID_ROUTING_COUNT, true, PDT_UNSIGNED_CHAR, 1, ReadLv3 | WriteLv3, (uint8_t)(6 << 4)),
new CallbackProperty<DeviceObject>(this, PID_PROG_MODE, true, PDT_BITSET8, 1, ReadLv3 | WriteLv3,
[](DeviceObject * io, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t
{
if (start == 0)
{
uint16_t currentNoOfElements = 1;
pushWord(currentNoOfElements, data);
return 1;
}
*data = io->_prgMode;
return 1;
},
[](DeviceObject * io, uint16_t start, uint8_t count, const uint8_t* data) -> uint8_t
{
if (start == 0)
return 1;
io->_prgMode = *data;
return 1;
}),
new DataProperty(PID_MAX_APDU_LENGTH, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t)254),
new CallbackProperty<DeviceObject>(this, PID_SUBNET_ADDR, false, PDT_UNSIGNED_CHAR, 1, ReadLv3 | WriteLv0,
[](DeviceObject * io, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t
{
if (start == 0)
{
uint16_t currentNoOfElements = 1;
pushWord(currentNoOfElements, data);
return 1;
}
*data = ((io->_ownAddress >> 8) & 0xff);
return 1;
}),
new CallbackProperty<DeviceObject>(this, PID_DEVICE_ADDR, false, PDT_UNSIGNED_CHAR, 1, ReadLv3 | WriteLv0,
[](DeviceObject * io, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t
{
if (start == 0)
{
uint16_t currentNoOfElements = 1;
pushWord(currentNoOfElements, data);
return 1;
}
*data = (io->_ownAddress & 0xff);
return 1;
}),
new DataProperty(PID_IO_LIST, false, PDT_UNSIGNED_INT, 8, ReadLv3 | WriteLv0),
new DataProperty(PID_HARDWARE_TYPE, true, PDT_GENERIC_06, 1, ReadLv3 | WriteLv3, hardwareType),
new DataProperty(PID_DEVICE_DESCRIPTOR, false, PDT_GENERIC_02, 1, ReadLv3 | WriteLv0),
#ifdef USE_RF
new DataProperty(PID_RF_DOMAIN_ADDRESS_CEMI_SERVER, true, PDT_GENERIC_06, 1, ReadLv3 | WriteLv3),
#endif
};
initializeProperties(sizeof(properties), properties);
}
uint8_t* DeviceObject::save(uint8_t* buffer)
{
buffer = pushWord(_ownAddress, buffer);
return InterfaceObject::save(buffer);
}
const uint8_t* DeviceObject::restore(const uint8_t* buffer)
{
buffer = popWord(_ownAddress, buffer);
return InterfaceObject::restore(buffer);
}
uint16_t DeviceObject::saveSize()
{
return 2 + InterfaceObject::saveSize();
}
uint16_t DeviceObject::individualAddress()
{
return _ownAddress;
}
void DeviceObject::individualAddress(uint16_t value)
{
_ownAddress = value;
}
#define USER_STOPPED 0x1
#define OWN_ADDR_DUPL 0x2
#define VERIFY_MODE 0x4
#define SAFE_STATE 0x8
void DeviceObject::individualAddressDuplication(bool value)
{
Property* prop = property(PID_DEVICE_CONTROL);
uint8_t data;
prop->read(data);
if (value)
data |= OWN_ADDR_DUPL;
else
data &= ~OWN_ADDR_DUPL;
prop->write(data);
}
bool DeviceObject::verifyMode()
{
Property* prop = property(PID_DEVICE_CONTROL);
uint8_t data;
prop->read(data);
return (data & VERIFY_MODE) > 0;
}
void DeviceObject::verifyMode(bool value)
{
Property* prop = property(PID_DEVICE_CONTROL);
uint8_t data;
prop->read(data);
if (value)
data |= VERIFY_MODE;
else
data &= ~VERIFY_MODE;
prop->write(data);
}
bool DeviceObject::progMode()
{
return _prgMode == 1;
}
void DeviceObject::progMode(bool value)
{
if (value)
_prgMode = 1;
else
_prgMode = 0;
}
uint16_t DeviceObject::manufacturerId()
{
uint16_t manufacturerId;
popWord(manufacturerId, propertyData(PID_SERIAL_NUMBER));
return manufacturerId;
}
void DeviceObject::manufacturerId(uint16_t value)
{
uint8_t data[LEN_KNX_SERIAL];
memcpy(data, propertyData(PID_SERIAL_NUMBER), LEN_KNX_SERIAL);
pushWord(value, data);
propertyValue(PID_SERIAL_NUMBER, data);
}
uint32_t DeviceObject::bauNumber()
{
uint32_t bauNumber;
popInt(bauNumber, propertyData(PID_SERIAL_NUMBER) + 2);
return bauNumber;
}
void DeviceObject::bauNumber(uint32_t value)
{
uint8_t data[LEN_KNX_SERIAL];
memcpy(data, propertyData(PID_SERIAL_NUMBER), LEN_KNX_SERIAL);
pushInt(value, data + 2);
propertyValue(PID_SERIAL_NUMBER, data);
}
const uint8_t* DeviceObject::orderNumber()
{
DataProperty* prop = (DataProperty*)property(PID_ORDER_INFO);
return prop->data();
}
void DeviceObject::orderNumber(const uint8_t* value)
{
Property* prop = property(PID_ORDER_INFO);
prop->write(value);
}
const uint8_t* DeviceObject::hardwareType()
{
DataProperty* prop = (DataProperty*)property(PID_HARDWARE_TYPE);
return prop->data();
}
void DeviceObject::hardwareType(const uint8_t* value)
{
Property* prop = property(PID_HARDWARE_TYPE);
prop->write(value);
}
uint16_t DeviceObject::version()
{
Property* prop = property(PID_VERSION);
uint16_t value;
prop->read(value);
return value;
}
void DeviceObject::version(uint16_t value)
{
Property* prop = property(PID_VERSION);
prop->write(value);
}
uint16_t DeviceObject::maskVersion()
{
Property* prop = property(PID_DEVICE_DESCRIPTOR);
uint16_t value;
prop->read(value);
return value;
}
void DeviceObject::maskVersion(uint16_t value)
{
Property* prop = property(PID_DEVICE_DESCRIPTOR);
prop->write(value);
}
uint16_t DeviceObject::maxApduLength()
{
Property* prop = property(PID_MAX_APDU_LENGTH);
uint16_t value;
prop->read(value);
return value;
}
void DeviceObject::maxApduLength(uint16_t value)
{
Property* prop = property(PID_MAX_APDU_LENGTH);
prop->write(value);
}
const uint8_t* DeviceObject::rfDomainAddress()
{
DataProperty* prop = (DataProperty*)property(PID_RF_DOMAIN_ADDRESS_CEMI_SERVER);
return prop->data();
}
void DeviceObject::rfDomainAddress(uint8_t* value)
{
Property* prop = property(PID_RF_DOMAIN_ADDRESS_CEMI_SERVER);
prop->write(value);
}
uint8_t DeviceObject::defaultHopCount()
{
Property* prop = property(PID_ROUTING_COUNT);
uint8_t value;
prop->read(value);
return (value >> 4) & 0x07;
}

View File

@ -0,0 +1,51 @@
#pragma once
#include "interface_object.h"
#define LEN_HARDWARE_TYPE 6
class DeviceObject: public InterfaceObject
{
public:
// increase this version anytime DeviceObject-API changes
// the following value represents the serialized representation of DeviceObject.
const uint16_t apiVersion = 1;
DeviceObject();
uint8_t* save(uint8_t* buffer) override;
const uint8_t* restore(const uint8_t* buffer) override;
uint16_t saveSize() override;
uint16_t individualAddress();
void individualAddress(uint16_t value);
void individualAddressDuplication(bool value);
bool verifyMode();
void verifyMode(bool value);
bool progMode();
void progMode(bool value);
uint16_t manufacturerId();
void manufacturerId(uint16_t value);
uint32_t bauNumber();
void bauNumber(uint32_t value);
const uint8_t* orderNumber();
void orderNumber(const uint8_t* value);
const uint8_t* hardwareType();
void hardwareType(const uint8_t* value);
uint16_t version();
void version(uint16_t value);
uint16_t maskVersion();
void maskVersion(uint16_t value);
uint16_t maxApduLength();
void maxApduLength(uint16_t value);
const uint8_t* rfDomainAddress();
void rfDomainAddress(uint8_t* value);
uint8_t defaultHopCount();
private:
uint8_t _prgMode = 0;
#if MASK_VERSION == 0x091A || MASK_VERSION == 0x2920
uint16_t _ownAddress = 0xFF00; // 15.15.0; couplers have 15.15.0 as default PA
#else
uint16_t _ownAddress = 0xFFFF; // 15.15.255;
#endif
};

View File

@ -0,0 +1,23 @@
#include "dpt.h"
#include "bits.h"
Dpt::Dpt()
{}
Dpt::Dpt(short mainGroup, short subGroup, short index /* = 0 */)
: mainGroup(mainGroup), subGroup(subGroup), index(index)
{
if (subGroup == 0 && (mainGroup < 14 || mainGroup > 16))
println("WARNING: You used an invalid Dpt *.0");
}
bool Dpt::operator==(const Dpt& other) const
{
return other.mainGroup == mainGroup && other.subGroup == subGroup && other.index == index;
}
bool Dpt::operator!=(const Dpt& other) const
{
return !(other == *this);
}

View File

@ -0,0 +1,373 @@
#pragma once
#define DPT_Switch Dpt(1, 1)
#define DPT_Bool Dpt(1, 2)
#define DPT_Enable Dpt(1, 3)
#define DPT_Ramp Dpt(1, 4)
#define DPT_Alarm Dpt(1, 5)
#define DPT_BinaryValue Dpt(1, 6)
#define DPT_Step Dpt(1, 7)
#define DPT_UpDown Dpt(1, 8)
#define DPT_OpenClose Dpt(1, 9)
#define DPT_Start Dpt(1, 10)
#define DPT_State Dpt(1, 11)
#define DPT_Invert Dpt(1, 12)
#define DPT_DimSendStyle Dpt(1, 13)
#define DPT_InputSource Dpt(1, 14)
#define DPT_Reset Dpt(1, 15)
#define DPT_Ack Dpt(1, 16)
#define DPT_Trigger Dpt(1, 17)
#define DPT_Occupancy Dpt(1, 18)
#define DPT_Window_Door Dpt(1, 19)
#define DPT_LogicalFunction Dpt(1, 21)
#define DPT_Scene_AB Dpt(1, 22)
#define DPT_ShutterBlinds_Mode Dpt(1, 23)
#define DPT_Heat_Cool Dpt(1, 100)
#define DPT_Switch_Control Dpt(2, 1)
#define DPT_Bool_Control Dpt(2, 2)
#define DPT_Enable_Control Dpt(2, 3)
#define DPT_Ramp_Control Dpt(2, 4)
#define DPT_Alarm_Control Dpt(2, 5)
#define DPT_BinaryValue_Control Dpt(2, 6)
#define DPT_Step_Control Dpt(2, 7)
#define DPT_Direction1_Control Dpt(2, 8)
#define DPT_Direction2_Control Dpt(2, 9)
#define DPT_Start_Control Dpt(2, 10)
#define DPT_State_Control Dpt(2, 11)
#define DPT_Invert_Control Dpt(2, 12)
#define DPT_Control_Dimming Dpt(3, 7)
#define DPT_Control_Blinds Dpt(3, 8)
#define DPT_Char_ASCII Dpt(4, 1)
#define DPT_Char_8859_1 Dpt(4, 2)
#define DPT_Scaling Dpt(5, 1)
#define DPT_Angle Dpt(5, 3)
#define DPT_Percent_U8 Dpt(5, 4)
#define DPT_DecimalFactor Dpt(5, 5)
#define DPT_Tariff Dpt(5, 6)
#define DPT_Value_1_Ucount Dpt(5, 10)
#define DPT_Percent_V8 Dpt(6, 1)
#define DPT_Value_1_Count Dpt(6, 10)
#define DPT_Status_Mode3 Dpt(6, 20)
#define DPT_Value_2_Ucount Dpt(7, 1)
#define DPT_TimePeriodMsec Dpt(7, 2)
#define DPT_TimePeriod10MSec Dpt(7, 3)
#define DPT_TimePeriod100MSec Dpt(7, 4)
#define DPT_TimePeriodSec Dpt(7, 5)
#define DPT_TimePeriodMin Dpt(7, 6)
#define DPT_TimePeriodHrs Dpt(7, 7)
#define DPT_PropDataType Dpt(7, 10)
#define DPT_Length_mm Dpt(7, 11)
#define DPT_UElCurrentmA Dpt(7, 12)
#define DPT_Brightness Dpt(7, 13)
#define DPT_Value_2_Count Dpt(8, 1)
#define DPT_DeltaTimeMsec Dpt(8, 2)
#define DPT_DeltaTime10MSec Dpt(8, 3)
#define DPT_DeltaTime100MSec Dpt(8, 4)
#define DPT_DeltaTimeSec Dpt(8, 5)
#define DPT_DeltaTimeMin Dpt(8, 6)
#define DPT_DeltaTimeHrs Dpt(8, 7)
#define DPT_Percent_V16 Dpt(8, 10)
#define DPT_Rotation_Angle Dpt(8, 11)
#define DPT_Value_Temp Dpt(9, 1)
#define DPT_Value_Tempd Dpt(9, 2)
#define DPT_Value_Tempa Dpt(9, 3)
#define DPT_Value_Lux Dpt(9, 4)
#define DPT_Value_Wsp Dpt(9, 5)
#define DPT_Value_Pres Dpt(9, 6)
#define DPT_Value_Humidity Dpt(9, 7)
#define DPT_Value_AirQuality Dpt(9, 8)
#define DPT_Value_Time1 Dpt(9, 10)
#define DPT_Value_Time2 Dpt(9, 11)
#define DPT_Value_Volt Dpt(9, 20)
#define DPT_Value_Curr Dpt(9, 21)
#define DPT_PowerDensity Dpt(9, 22)
#define DPT_KelvinPerPercent Dpt(9, 23)
#define DPT_Power Dpt(9, 24)
#define DPT_Value_Volume_Flow Dpt(9, 25)
#define DPT_Rain_Amount Dpt(9, 26)
#define DPT_Value_Temp_F Dpt(9, 27)
#define DPT_Value_Wsp_kmh Dpt(9, 28)
#define DPT_TimeOfDay Dpt(10, 1, 1)
#define DPT_Date Dpt(11, 1)
#define DPT_Value_4_Ucount Dpt(12, 1)
#define DPT_Value_4_Count Dpt(13, 1)
#define DPT_FlowRate_m3_per_h Dpt(13, 2)
#define DPT_ActiveEnergy Dpt(13, 10)
#define DPT_ApparantEnergy Dpt(13, 11)
#define DPT_ReactiveEnergy Dpt(13, 12)
#define DPT_ActiveEnergy_kWh Dpt(13, 13)
#define DPT_ApparantEnergy_kVAh Dpt(13, 14)
#define DPT_ReactiveEnergy_kVARh Dpt(13, 15)
#define DPT_LongDeltaTimeSec Dpt(13, 100)
#define DPT_Value_Acceleration Dpt(14, 0)
#define DPT_Value_Acceleration_Angular Dpt(14, 1)
#define DPT_Value_Activation_Energy Dpt(14, 2)
#define DPT_Value_Activity Dpt(14, 3)
#define DPT_Value_Mol Dpt(14, 4)
#define DPT_Value_Amplitude Dpt(14, 5)
#define DPT_Value_AngleRad Dpt(14, 6)
#define DPT_Value_AngleDeg Dpt(14, 7)
#define DPT_Value_Angular_Momentum Dpt(14, 8)
#define DPT_Value_Angular_Velocity Dpt(14, 9)
#define DPT_Value_Area Dpt(14, 10)
#define DPT_Value_Capacitance Dpt(14, 11)
#define DPT_Value_Charge_DensitySurface Dpt(14, 12)
#define DPT_Value_Charge_DensityVolume Dpt(14, 13)
#define DPT_Value_Compressibility Dpt(14, 14)
#define DPT_Value_Conductance Dpt(14, 15)
#define DPT_Value_Electrical_Conductivity Dpt(14, 16)
#define DPT_Value_Density Dpt(14, 17)
#define DPT_Value_Electric_Charge Dpt(14, 18)
#define DPT_Value_Electric_Current Dpt(14, 19)
#define DPT_Value_Electric_CurrentDensity Dpt(14, 20)
#define DPT_Value_Electric_DipoleMoment Dpt(14, 21)
#define DPT_Value_Electric_Displacement Dpt(14, 22)
#define DPT_Value_Electric_FieldStrength Dpt(14, 23)
#define DPT_Value_Electric_Flux Dpt(14, 24)
#define DPT_Value_Electric_FluxDensity Dpt(14, 25)
#define DPT_Value_Electric_Polarization Dpt(14, 26)
#define DPT_Value_Electric_Potential Dpt(14, 27)
#define DPT_Value_Electric_PotentialDifference Dpt(14, 28)
#define DPT_Value_ElectromagneticMoment Dpt(14, 29)
#define DPT_Value_Electromotive_Force Dpt(14, 30)
#define DPT_Value_Energy Dpt(14, 31)
#define DPT_Value_Force Dpt(14, 32)
#define DPT_Value_Frequency Dpt(14, 33)
#define DPT_Value_Angular_Frequency Dpt(14, 34)
#define DPT_Value_Heat_Capacity Dpt(14, 35)
#define DPT_Value_Heat_FlowRate Dpt(14, 36)
#define DPT_Value_Heat_Quantity Dpt(14, 37)
#define DPT_Value_Impedance Dpt(14, 38)
#define DPT_Value_Length Dpt(14, 39)
#define DPT_Value_Light_Quantity Dpt(14, 40)
#define DPT_Value_Luminance Dpt(14, 41)
#define DPT_Value_Luminous_Flux Dpt(14, 42)
#define DPT_Value_Luminous_Intensity Dpt(14, 43)
#define DPT_Value_Magnetic_FieldStrength Dpt(14, 44)
#define DPT_Value_Magnetic_Flux Dpt(14, 45)
#define DPT_Value_Magnetic_FluxDensity Dpt(14, 46)
#define DPT_Value_Magnetic_Moment Dpt(14, 47)
#define DPT_Value_Magnetic_Polarization Dpt(14, 48)
#define DPT_Value_Magnetization Dpt(14, 49)
#define DPT_Value_MagnetomotiveForce Dpt(14, 50)
#define DPT_Value_Mass Dpt(14, 51)
#define DPT_Value_MassFlux Dpt(14, 52)
#define DPT_Value_Momentum Dpt(14, 53)
#define DPT_Value_Phase_AngleRad Dpt(14, 54)
#define DPT_Value_Phase_AngleDeg Dpt(14, 55)
#define DPT_Value_Power Dpt(14, 56)
#define DPT_Value_Power_Factor Dpt(14, 57)
#define DPT_Value_Pressure Dpt(14, 58)
#define DPT_Value_Reactance Dpt(14, 59)
#define DPT_Value_Resistance Dpt(14, 60)
#define DPT_Value_Resistivity Dpt(14, 61)
#define DPT_Value_SelfInductance Dpt(14, 62)
#define DPT_Value_SolidAngle Dpt(14, 63)
#define DPT_Value_Sound_Intensity Dpt(14, 64)
#define DPT_Value_Speed Dpt(14, 65)
#define DPT_Value_Stress Dpt(14, 66)
#define DPT_Value_Surface_Tension Dpt(14, 67)
#define DPT_Value_Common_Temperature Dpt(14, 68)
#define DPT_Value_Absolute_Temperature Dpt(14, 69)
#define DPT_Value_TemperatureDifference Dpt(14, 70)
#define DPT_Value_Thermal_Capacity Dpt(14, 71)
#define DPT_Value_Thermal_Conductivity Dpt(14, 72)
#define DPT_Value_ThermoelectricPower Dpt(14, 73)
#define DPT_Value_Time Dpt(14, 74)
#define DPT_Value_Torque Dpt(14, 75)
#define DPT_Value_Volume Dpt(14, 76)
#define DPT_Value_Volume_Flux Dpt(14, 77)
#define DPT_Value_Weight Dpt(14, 78)
#define DPT_Value_Work Dpt(14, 79)
#define DPT_Value_ApparentPower Dpt(14, 80)
#define DPT_Access_Data Dpt(15, 0)
#define DPT_String_ASCII Dpt(16, 0)
#define DPT_String_8859_1 Dpt(16, 1)
#define DPT_SceneNumber Dpt(17, 1)
#define DPT_SceneControl Dpt(18, 1)
#define DPT_DateTime Dpt(19, 1)
#define DPT_SCLOMode Dpt(20, 1)
#define DPT_BuildingMode Dpt(20, 2)
#define DPT_OccMode Dpt(20, 3)
#define DPT_Priority Dpt(20, 4)
#define DPT_LightApplicationMode Dpt(20, 5)
#define DPT_ApplicationArea Dpt(20, 6)
#define DPT_AlarmClassType Dpt(20, 7)
#define DPT_PSUMode Dpt(20, 8)
#define DPT_ErrorClass_System Dpt(20, 11)
#define DPT_ErrorClass_HVAC Dpt(20, 12)
#define DPT_Time_Delay Dpt(20, 13)
#define DPT_Beaufort_Wind_Force_Scale Dpt(20, 14)
#define DPT_SensorSelect Dpt(20, 17)
#define DPT_ActuatorConnectType Dpt(20, 20)
#define DPT_FuelType Dpt(20, 100)
#define DPT_BurnerType Dpt(20, 101)
#define DPT_HVACMode Dpt(20, 102)
#define DPT_DHWMode Dpt(20, 103)
#define DPT_LoadPriority Dpt(20, 104)
#define DPT_HVACContrMode Dpt(20, 105)
#define DPT_HVACEmergMode Dpt(20, 106)
#define DPT_ChangeoverMode Dpt(20, 107)
#define DPT_ValveMode Dpt(20, 108)
#define DPT_DamperMode Dpt(20, 109)
#define DPT_HeaterMode Dpt(20, 110)
#define DPT_FanMode Dpt(20, 111)
#define DPT_MasterSlaveMode Dpt(20, 112)
#define DPT_StatusRoomSetp Dpt(20, 113)
#define DPT_ADAType Dpt(20, 120)
#define DPT_BackupMode Dpt(20, 121)
#define DPT_StartSynchronization Dpt(20, 122)
#define DPT_Behaviour_Lock_Unlock Dpt(20, 600)
#define DPT_Behaviour_Bus_Power_Up_Down Dpt(20, 601)
#define DPT_DALI_Fade_Time Dpt(20, 602)
#define DPT_BlinkingMode Dpt(20, 603)
#define DPT_LightControlMode Dpt(20, 604)
#define DPT_SwitchPBModel Dpt(20, 605)
#define DPT_PBAction Dpt(20, 606)
#define DPT_DimmPBModel Dpt(20, 607)
#define DPT_SwitchOnMode Dpt(20, 608)
#define DPT_LoadTypeSet Dpt(20, 609)
#define DPT_LoadTypeDetected Dpt(20, 610)
#define DPT_SABExceptBehaviour Dpt(20, 801)
#define DPT_SABBehaviour_Lock_Unlock Dpt(20, 802)
#define DPT_SSSBMode Dpt(20, 803)
#define DPT_BlindsControlMode Dpt(20, 804)
#define DPT_CommMode Dpt(20, 1000)
#define DPT_AddInfoTypes Dpt(20, 1001)
#define DPT_RF_ModeSelect Dpt(20, 1002)
#define DPT_RF_FilterSelect Dpt(20, 1003)
#define DPT_StatusGen Dpt(21, 1)
#define DPT_Device_Control Dpt(21, 2)
#define DPT_ForceSign Dpt(21, 100)
#define DPT_ForceSignCool Dpt(21, 101)
#define DPT_StatusRHC Dpt(21, 102)
#define DPT_StatusSDHWC Dpt(21, 103)
#define DPT_FuelTypeSet Dpt(21, 104)
#define DPT_StatusRCC Dpt(21, 105)
#define DPT_StatusAHU Dpt(21, 106)
#define DPT_LightActuatorErrorInfo Dpt(21, 601)
#define DPT_RF_ModeInfo Dpt(21, 1000)
#define DPT_RF_FilterInfo Dpt(21, 1001)
#define DPT_Channel_Activation_8 Dpt(21, 1010)
#define DPT_StatusDHWC Dpt(22, 100)
#define DPT_StatusRHCC Dpt(22, 101)
#define DPT_Media Dpt(22, 1000)
#define DPT_Channel_Activation_16 Dpt(22, 1010)
#define DPT_OnOff_Action Dpt(23, 1)
#define DPT_Alarm_Reaction Dpt(23, 2)
#define DPT_UpDown_Action Dpt(23, 3)
#define DPT_HVAC_PB_Action Dpt(23, 102)
#define DPT_VarString_8859_1 Dpt(24, 1)
#define DPT_DoubleNibble Dpt(25, 1000)
#define DPT_SceneInfo Dpt(26, 1)
#define DPT_CombinedInfoOnOff Dpt(27, 1)
#define DPT_UTF_8 Dpt(28, 1)
#define DPT_ActiveEnergy_V64 Dpt(29, 10)
#define DPT_ApparantEnergy_V64 Dpt(29, 11)
#define DPT_ReactiveEnergy_V64 Dpt(29, 12)
#define DPT_Channel_Activation_24 Dpt(30, 1010)
#define DPT_PB_Action_HVAC_Extended Dpt(31, 101)
#define DPT_Heat_Cool_Z Dpt(200, 100)
#define DPT_BinaryValue_Z Dpt(200, 101)
#define DPT_HVACMode_Z Dpt(201, 100)
#define DPT_DHWMode_Z Dpt(201, 102)
#define DPT_HVACContrMode_Z Dpt(201, 104)
#define DPT_EnablH_Cstage_Z Dpt(201, 105)
#define DPT_BuildingMode_Z Dpt(201, 107)
#define DPT_OccMode_Z Dpt(201, 108)
#define DPT_HVACEmergMode_Z Dpt(201, 109)
#define DPT_RelValue_Z Dpt(202, 1)
#define DPT_UCountValue8_Z Dpt(202, 2)
#define DPT_TimePeriodMsec_Z Dpt(203, 2)
#define DPT_TimePeriod10Msec_Z Dpt(203, 3)
#define DPT_TimePeriod100Msec_Z Dpt(203, 4)
#define DPT_TimePeriodSec_Z Dpt(203, 5)
#define DPT_TimePeriodMin_Z Dpt(203, 6)
#define DPT_TimePeriodHrs_Z Dpt(203, 7)
#define DPT_UFlowRateLiter_per_h_Z Dpt(203, 11)
#define DPT_UCountValue16_Z Dpt(203, 12)
#define DPT_UElCurrent_Z Dpt(203, 13)
#define DPT_PowerKW_Z Dpt(203, 14)
#define DPT_AtmPressureAbs_Z Dpt(203, 15)
#define DPT_PercentU16_Z Dpt(203, 17)
#define DPT_HVACAirQual_Z Dpt(203, 100)
#define DPT_WindSpeed_Z Dpt(203, 101)
#define DPT_SunIntensity_Z Dpt(203, 102)
#define DPT_HVACAirFlowAbs_Z Dpt(203, 104)
#define DPT_RelSignedValue_Z Dpt(204, 1)
#define DPT_DeltaTimeMsec_Z Dpt(205, 2)
#define DPT_DeltaTime10Msec_Z Dpt(205, 3)
#define DPT_DeltaTime100Msec_Z Dpt(205, 4)
#define DPT_DeltaTimeSec_Z Dpt(205, 5)
#define DPT_DeltaTimeMin_Z Dpt(205, 6)
#define DPT_DeltaTimeHrs_Z Dpt(205, 7)
#define DPT_Percent_V16_Z Dpt(205, 17)
#define DPT_TempHVACAbs_Z Dpt(205, 100)
#define DPT_TempHVACRel_Z Dpt(205, 101)
#define DPT_HVACAirFlowRel_Z Dpt(205, 102)
#define DPT_HVACModeNext Dpt(206, 100)
#define DPT_DHWModeNext Dpt(206, 102)
#define DPT_OccModeNext Dpt(206, 104)
#define DPT_BuildingModeNext Dpt(206, 105)
#define DPT_StatusBUC Dpt(207, 100)
#define DPT_LockSign Dpt(207, 101)
#define DPT_ValueDemBOC Dpt(207, 102)
#define DPT_ActPosDemAbs Dpt(207, 104)
#define DPT_StatusAct Dpt(207, 105)
#define DPT_StatusLightingActuator Dpt(207, 600)
#define DPT_StatusHPM Dpt(209, 100)
#define DPT_TempRoomDemAbs Dpt(209, 101)
#define DPT_StatusCPM Dpt(209, 102)
#define DPT_StatusWTC Dpt(209, 103)
#define DPT_TempFlowWaterDemAbs Dpt(210, 100)
#define DPT_EnergyDemWater Dpt(211, 100)
#define DPT_TempRoomSetpSetShift_3 Dpt(212, 100)
#define DPT_TempRoomSetpSet_3 Dpt(212, 101)
#define DPT_TempRoomSetpSet_4 Dpt(213, 100)
#define DPT_TempDHWSetpSet_4 Dpt(213, 101)
#define DPT_TempRoomSetpSetShift_4 Dpt(213, 102)
#define DPT_PowerFlowWaterDemHPM Dpt(214, 100)
#define DPT_PowerFlowWaterDemCPM Dpt(214, 101)
#define DPT_StatusBOC Dpt(215, 100)
#define DPT_StatusCC Dpt(215, 101)
#define DPT_SpecHeatProd Dpt(216, 100)
#define DPT_Version Dpt(217, 1)
#define DPT_VolumeLiter_Z Dpt(218, 1)
#define DPT_FlowRate_m3_per_h_Z Dpt(218, 2)
#define DPT_AlarmInfo Dpt(219, 1)
#define DPT_TempHVACAbsNext Dpt(220, 100)
#define DPT_SerNum Dpt(221, 1)
#define DPT_TempRoomSetpSetF16_3 Dpt(222, 100)
#define DPT_TempRoomSetpSetShiftF16_3 Dpt(222, 101)
#define DPT_EnergyDemAir Dpt(223, 100)
#define DPT_TempSupplyAirSetpSet Dpt(224, 100)
#define DPT_ScalingSpeed Dpt(225, 1)
#define DPT_Scaling_Step_Time Dpt(225, 2)
#define DPT_TariffNext Dpt(225, 3)
#define DPT_MeteringValue Dpt(229, 1)
#define DPT_MBus_Address Dpt(230, 1000)
#define DPT_Locale_ASCII Dpt(231, 1)
#define DPT_Colour_RGB Dpt(232, 600)
#define DPT_LanguageCodeAlpha2_ASCII Dpt(234, 1)
#define DPT_RegionCodeAlpha2_ASCII Dpt(234, 2)
#define DPT_Tariff_ActiveEnergy Dpt(235, 1)
#define DPT_Prioritised_Mode_Control Dpt(236, 1)
#define DPT_DALI_Control_Gear_Diagnostic Dpt(237, 600)
#define DPT_SceneConfig Dpt(238, 1)
#define DPT_DALI_Diagnostics Dpt(238, 600)
#define DPT_FlaggedScaling Dpt(239, 1)
#define DPT_CombinedPosition Dpt(240, 800)
#define DPT_StatusSAB Dpt(241, 800)
#define DPT_Colour_RGBW Dpt(251, 600)
class Dpt
{
public:
Dpt();
Dpt(short mainGroup, short subGroup, short index = 0);
unsigned short mainGroup;
unsigned short subGroup;
unsigned short index;
bool operator==(const Dpt& other) const;
bool operator!=(const Dpt& other) const;
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,149 @@
/*
KNX client library - internals
Copyright (C) 2005-2011 Martin Koegler <mkoegler@auto.tuwien.ac.at>
Copyright (C) 2014 Patrik Pfaffenbauer <patrik.pfaffenbauer@p3.co.at>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
In addition to the permissions in the GNU General Public License,
you may link the compiled version of this file into combinations
with other programs, and distribute those combinations without any
restriction coming from the use of this file. (The General Public
License restrictions do apply in other respects; for example, they
cover modification of the file, and distribution when not linked into
a combine executable.)
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#pragma once
#include <cstdint>
#include "dpt.h"
#include "knx_value.h"
/**
* Converts the KNX Payload given by the specific DPT and puts the value in the KNXValue struc
*/
bool KNX_Decode_Value(uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
/**
* Converts the KNXValue struct to the KNX Payload as the specific DPT
*/
bool KNX_Encode_Value(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
//KNX to internal
bool busValueToBinary(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToBinaryControl(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToStepControl(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToCharacter(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToUnsigned8(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToSigned8(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToStatusAndMode(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToUnsigned16(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToTimePeriod(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToSigned16(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToTimeDelta(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToFloat16(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToTime(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToDate(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToUnsigned32(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToSigned32(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToLongTimePeriod(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToFloat32(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToAccess(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToString(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToScene(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToSceneControl(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToSceneInfo(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToSceneConfig(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToDateTime(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToUnicode(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToSigned64(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToAlarmInfo(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToSerialNumber(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToVersion(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToScaling(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToTariff(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToLocale(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToRGB(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToRGBW(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToFlaggedScaling(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
bool busValueToActiveEnergy(const uint8_t* payload, size_t payload_length, const Dpt& datatype, KNXValue& value);
//Internal to KNX
bool valueToBusValueBinary(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueBinaryControl(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueStepControl(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueCharacter(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueUnsigned8(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueSigned8(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueStatusAndMode(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueUnsigned16(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueTimePeriod(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueSigned16(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueTimeDelta(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueFloat16(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueTime(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueDate(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueUnsigned32(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueSigned32(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueLongTimePeriod(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueFloat32(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueAccess(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueString(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueScene(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueSceneControl(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueSceneInfo(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueSceneConfig(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueDateTime(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueUnicode(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueSigned64(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueAlarmInfo(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueSerialNumber(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueVersion(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueScaling(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueTariff(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueLocale(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueRGB(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueRGBW(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueFlaggedScaling(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
bool valueToBusValueActiveEnergy(const KNXValue& value, uint8_t* payload, size_t payload_length, const Dpt& datatype);
//Payload manipulation
bool bitFromPayload(const uint8_t* payload, int index);
uint8_t unsigned8FromPayload(const uint8_t* payload, int index);
int8_t signed8FromPayload(const uint8_t* payload, int index);
uint16_t unsigned16FromPayload(const uint8_t* payload, int index);
int16_t signed16FromPayload(const uint8_t* payload, int index);
uint32_t unsigned32FromPayload(const uint8_t* payload, int index);
int32_t signed32FromPayload(const uint8_t* payload, int index);
uint64_t unsigned64FromPayload(const uint8_t* payload, int index);
double float16FromPayload(const uint8_t* payload, int index);
float float32FromPayload(const uint8_t* payload, int index);
double float64FromPayload(const uint8_t* payload, int index);
int64_t signed64FromPayload(const uint8_t* payload, int index);
uint8_t bcdFromPayload(const uint8_t* payload, int index);
void bitToPayload(uint8_t* payload, size_t payload_length, int index, bool value);
void unsigned8ToPayload(uint8_t* payload, size_t payload_length, int index, uint8_t value, uint8_t mask); //mask 0xFF
void signed8ToPayload(uint8_t* payload, size_t payload_length, int index, int8_t value, uint8_t mask); //mask 0xFF
void unsigned16ToPayload(uint8_t* payload, size_t payload_length, int index, uint16_t value, uint16_t mask); //mask 0xFFFF
void signed16ToPayload(uint8_t* payload, size_t payload_length, int index, int16_t value, uint16_t mask); //mask 0xFFFF
void unsigned32ToPayload(uint8_t* payload, size_t payload_length, int index, uint32_t value, uint32_t mask); //mask = 0xFFFFFFFF
void signed32ToPayload(uint8_t* payload, size_t payload_length, int index, int32_t value, uint32_t mask); //mask = 0xFFFFFFFF
void float16ToPayload(uint8_t* payload, size_t payload_length, int index, double value, uint16_t mask); //mask = 0xFFFF
void float32ToPayload(uint8_t* payload, size_t payload_length, int index, double value, uint32_t mask); //mask = 0xFFFFFFFF
void signed64ToPayload(uint8_t* payload, size_t payload_length, int index, int64_t value, uint64_t mask); //mask = UINT64_C(0xFFFFFFFFFFFFFFFF)
void bcdToPayload(uint8_t* payload, size_t payload_length, int index, uint8_t value);

View File

@ -0,0 +1,53 @@
#pragma once
#include "property.h"
class InterfaceObject;
template <class T> class FunctionProperty : public Property
{
public:
FunctionProperty(T* io, PropertyID id,
void (*commandCallback)(T*, uint8_t*, uint8_t, uint8_t*, uint8_t&),
void (*stateCallback)(T*, uint8_t*, uint8_t, uint8_t*, uint8_t&))
: Property(id, false, PDT_FUNCTION, 1, ReadLv0 | WriteLv0), _interfaceObject(io), _commandCallback(commandCallback), _stateCallback(stateCallback)
/* max_elements is set to 1, read and write level any value so we use Lv0, see 3.3.7 Application Layer p.68 */
{}
uint8_t read(uint16_t start, uint8_t count, uint8_t* data) const override
{
return 0;
}
uint8_t write(uint16_t start, uint8_t count, const uint8_t* data) override
{
return 0;
}
void command(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) override
{
if (length == 0 || _commandCallback == nullptr )
{
resultLength = 0;
return;
}
_commandCallback(_interfaceObject, data, length, resultData, resultLength);
}
void state(uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength) override
{
if (length == 0 || _stateCallback == nullptr )
{
resultLength = 0;
return;
}
_stateCallback(_interfaceObject, data, length, resultData, resultLength);
}
private:
T* _interfaceObject = nullptr;
void (*_commandCallback)(T*, uint8_t*, uint8_t, uint8_t*, uint8_t&) = nullptr;
void (*_stateCallback)(T*, uint8_t*, uint8_t, uint8_t*, uint8_t&) = nullptr;
};

View File

@ -0,0 +1,356 @@
#include "group_object.h"
#include "bits.h"
#include "string.h"
#include "datapoint_types.h"
#include "group_object_table_object.h"
#ifdef SMALL_GROUPOBJECT
GroupObjectUpdatedHandler GroupObject::_updateHandlerStatic = 0;
#endif
GroupObjectTableObject* GroupObject::_table = 0;
GroupObject::GroupObject()
{
_data = 0;
_uninitialized = true;
_commFlag = Uninitialized;
_dataLength = 0;
#ifndef SMALL_GROUPOBJECT
_updateHandler = 0;
#endif
}
GroupObject::~GroupObject()
{
if (_data)
delete[] _data;
}
bool GroupObject::responseUpdateEnable()
{
if (!_table)
return false;
return bitRead(ntohs(_table->_tableData[_asap]), 15) > 0;
}
bool GroupObject::transmitEnable()
{
if (!_table)
return false;
return bitRead(ntohs(_table->_tableData[_asap]), 14) > 0 ;
}
bool GroupObject::valueReadOnInit()
{
if (!_table)
return false;
return bitRead(ntohs(_table->_tableData[_asap]), 13) > 0;
}
bool GroupObject::writeEnable()
{
if (!_table)
return false;
return bitRead(ntohs(_table->_tableData[_asap]), 12) > 0 ;
}
bool GroupObject::readEnable()
{
if (!_table)
return false;
// we forbid reading of new (uninitialized) go
if (_uninitialized)
return false;
return bitRead(ntohs(_table->_tableData[_asap]), 11) > 0;
}
bool GroupObject::communicationEnable()
{
if (!_table)
return false;
return bitRead(ntohs(_table->_tableData[_asap]), 10) > 0;
}
Priority GroupObject::priority()
{
if (!_table)
return LowPriority;
return (Priority)((ntohs(_table->_tableData[_asap]) >> 6) & (3 << 2)) ;
}
uint8_t* GroupObject::valueRef()
{
return _data;
}
uint16_t GroupObject::asap()
{
return _asap;
}
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) const
{
if (code < 7)
return 0;
if (code < 8)
return 1;
if (code < 11 || (code > 20 && code < 255))
return code - 6;
switch (code)
{
case 11:
return 6;
case 12:
return 8;
case 13:
return 10;
case 14:
return 14;
case 15:
return 5;
case 16:
return 7;
case 17:
return 9;
case 18:
return 11;
case 19:
return 12;
case 20:
return 13;
case 255:
return 252;
}
return -1;
}
ComFlag GroupObject::commFlag()
{
return _commFlag;
}
void GroupObject::commFlag(ComFlag value)
{
_commFlag = value;
if (value == WriteRequest || value == Updated || value == Ok)
_uninitialized = false;
}
bool GroupObject::initialized()
{
return !_uninitialized;
}
void GroupObject::requestObjectRead()
{
commFlag(ReadRequest);
}
void GroupObject::objectWritten()
{
commFlag(WriteRequest);
}
size_t GroupObject::valueSize()
{
return _dataLength;
}
size_t GroupObject::sizeInTelegram()
{
uint8_t code = lowByte(ntohs(_table->_tableData[_asap]));
return asapValueSize(code);
}
size_t GroupObject::sizeInMemory() const
{
uint8_t code = lowByte(ntohs(_table->_tableData[_asap]));
size_t result = asapValueSize(code);
if (result == 0)
return 1;
if (code == 14)
return 14 + 1;
return result;
}
#ifdef SMALL_GROUPOBJECT
GroupObjectUpdatedHandler GroupObject::classCallback()
{
return _updateHandlerStatic;
}
void GroupObject::classCallback(GroupObjectUpdatedHandler handler)
{
_updateHandlerStatic = handler;
}
void GroupObject::processClassCallback(GroupObject& ko)
{
if (_updateHandlerStatic != 0)
_updateHandlerStatic(ko);
}
#else
void GroupObject::callback(GroupObjectUpdatedHandler handler)
{
_updateHandler = handler;
}
GroupObjectUpdatedHandler GroupObject::callback()
{
return _updateHandler;
}
#endif
bool GroupObject::value(const KNXValue& value, const Dpt& type)
{
if (valueNoSend(value, type))
{
// write on successful conversion/setting value only
objectWritten();
return true;
}
return false;
}
KNXValue GroupObject::value(const Dpt& type)
{
KNXValue value = "";
KNX_Decode_Value(_data, _dataLength, type, value);
return value;
}
bool GroupObject::tryValue(KNXValue& value, const Dpt& type)
{
return KNX_Decode_Value(_data, _dataLength, type, value);
}
#ifndef SMALL_GROUPOBJECT
void GroupObject::dataPointType(Dpt value)
{
_datapointType = value;
}
Dpt GroupObject::dataPointType()
{
return _datapointType;
}
bool GroupObject::tryValue(KNXValue& value)
{
return tryValue(value, _datapointType);
}
bool GroupObject::value(const KNXValue& value)
{
return this->value(value, _datapointType);
}
KNXValue GroupObject::value()
{
return value(_datapointType);
}
bool GroupObject::valueNoSend(const KNXValue& value)
{
return valueNoSend(value, _datapointType);
}
#endif
bool GroupObject::valueNoSend(const KNXValue& value, const Dpt& type)
{
const bool encodingDone = KNX_Encode_Value(value, _data, _dataLength, type);
// initialize on succesful conversion only
if (encodingDone && _uninitialized)
commFlag(Ok);
return encodingDone;
}
bool GroupObject::valueNoSendCompare(const KNXValue& value, const Dpt& type)
{
if (_uninitialized)
{
// always set first value
return valueNoSend(value, type);
}
else
{
// convert new value to given DPT
uint8_t newData[_dataLength];
memset(newData, 0, _dataLength);
const bool encodingDone = KNX_Encode_Value(value, newData, _dataLength, type);
if (!encodingDone)
{
// value conversion to DPT failed
// do NOT update the value of the KO!
return false;
}
// check for change in converted value / update value on change only
const bool dataChanged = memcmp(_data, newData, _dataLength);
if (dataChanged)
memcpy(_data, newData, _dataLength);
return dataChanged;
}
}
bool GroupObject::valueCompare(const KNXValue& value, const Dpt& type)
{
if (valueNoSendCompare(value, type))
{
objectWritten();
return true;
}
return false;
}

View File

@ -0,0 +1,287 @@
#pragma once
#include <stddef.h>
#include <stdint.h>
#include "knx_types.h"
#include "dptconvert.h"
class GroupObjectTableObject;
enum ComFlag : uint8_t
{
Updated = 0, //!< Group object was updated
ReadRequest = 1, //!< Read was requested but was not processed
WriteRequest = 2, //!< Write was requested but was not processed
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
};
class GroupObject;
#ifndef HAS_FUNCTIONAL
#if defined(__linux__) || defined(ARDUINO_ARCH_ESP32) || defined(ESP_PLATFORM) || defined(ARDUINO_ARCH_STM32) || defined (ARDUINO_ARCH_SAMD) || defined (ARDUINO_ARCH_RP2040) || defined(LIBRETINY)
#define HAS_FUNCTIONAL 1
#else
#define HAS_FUNCTIONAL 0
#endif
#endif
#if HAS_FUNCTIONAL
#include <functional>
typedef std::function<void(GroupObject&)> GroupObjectUpdatedHandler;
#else
typedef void (*GroupObjectUpdatedHandler)(GroupObject& go);
#endif
/**
* This class represents a single group object. In german they are called "Kommunikationsobjekt" or "KO".
*/
class GroupObject
{
friend class GroupObjectTableObject;
GroupObject(const GroupObject& other) = delete;
public:
/**
* The constructor.
*/
GroupObject();
/**
* The destructor.
*/
virtual ~GroupObject();
// config flags from ETS
/**
* Check if the update flag (U) was set. (A-flag in german)
*/
bool responseUpdateEnable();
/**
* Check if the transmit flag (T) was set. (UE-flag in german)
*/
bool transmitEnable();
/**
* Check if the initialisation flag (I) was set.
*/
bool valueReadOnInit();
/**
* Check if the write flag (W) was set. (S-flag in german)
*/
bool writeEnable();
/**
* Check if the read flag (R) was set. (L-flag in german)
*/
bool readEnable();
/**
* Check if the communication flag (C) was set. (K-flag in german)
*/
bool communicationEnable();
/**
* Get the priority of the group object.
*/
Priority priority();
/**
* Return the current state of the group object. See ::ComFlag
*/
ComFlag commFlag();
/**
* Set the current state of the group object. Application code should only use this to set the state to ::Ok after
* reading a ::Updated to mark the changed group object as processed. This is optional.
*/
void commFlag(ComFlag value);
/**
* Check if the group object contains a valid value assigned from bus or from application program
*/
bool initialized();
/**
* Request the read of a communication object. Calling this function triggers the
* sending of a read-group-value telegram, to read the value of the communication
* object from the bus.
*
* When the answer is received, the communication object's value will be updated.
*
* This sets the state of the group objecte to ::ReadRequest
*/
void requestObjectRead();
/**
* Mark a communication object as written. Calling this
* function triggers the sending of a write-group-value telegram.
*
* This sets the state of the group object to ::WriteRequest
*/
void objectWritten();
/**
* returns the size of the group object in Byte. For Group objects with size smaller than 1 byte (for example Dpt 1) this method
* will return 1.
*/
size_t valueSize();
/**
* returns the size of the group object in Byte as it is in a telegram. For Group objects with size smaller than 1 byte (for example Dpt 1) this method
* 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.
*/
uint8_t* valueRef();
/**
* returns the Application Service Access Point of the group object. In reality this is just the number of the group object.
* (in german "KO-Nr")
*/
uint16_t asap();
#ifndef SMALL_GROUPOBJECT
/**
* register a callback for this group object. The registered callback will be called if the group object was changed from the bus.
*/
void callback(GroupObjectUpdatedHandler handler);
/**
* returns the registered callback
*/
GroupObjectUpdatedHandler callback();
#endif
/**
* return the current value of the group object.
* @param type the datapoint type used for the conversion. If this doesn't fit to the group object the returned value is invalid.
*/
KNXValue value(const Dpt& type);
/**
* set the current value of the group object and changes the state of the group object to ::WriteRequest.
* @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 was converted successfully to the datapoint type and the group object was updated.
*/
bool value(const KNXValue& value, const Dpt& type);
/**
* Check if the value (after conversion to dpt) will differ from current value of the group object and changes the state of the group object to ::WriteRequest if different.
* 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, false if conversion results in same value as stored in group object or failed.
*/
bool valueCompare(const KNXValue& value, const Dpt& type);
/**
* set the current value of the group objectand show success.
* @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 value was converted successfully to the datapoint type and the group object was updated.
*/
bool valueNoSend(const KNXValue& value, const Dpt& type);
/**
* Check if the value (after conversion to dpt) will differ from current value of the group object and update if necessary.
* Use this method only, when the value change is relevant, otherwise valueNoSend(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, false if conversion results in same value as stored in group object or failed.
*/
bool valueNoSendCompare(const KNXValue& value, const Dpt& type);
/**
* set the current value of the group object.
* @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 was changed successfully.
*/
bool tryValue(KNXValue& value, const Dpt& type);
#ifndef SMALL_GROUPOBJECT
/**
* return the current value of the group object. The datapoint type must be set with dataPointType(). Otherwise the returned
* value is invalid.
*/
KNXValue value();
/**
* set the current value of the group object and changes the state of the group object to ::WriteRequest.
* @param value the value the group object is set to
*
* The parameters must fit the group object and dhe datapoint type must be set with dataPointType(). Otherwise it will stay unchanged.
*
* @returns true if the value was converted successfully to the datapoint type and the group object was updated.
*/
bool value(const KNXValue& value);
/**
* set the current value of the group object.
* @param value the value the group object is set to
*
* The parameters must fit the group object and the datapoint type must be set with dataPointType(). Otherwise it will stay unchanged.
*
* @returns true if the value was converted successfully to the datapoint type and the group object was updated.
*/
bool valueNoSend(const KNXValue& value);
/**
* set the current value of the group object.
* @param value the value the group object is set to
*
* The parameters must fit the group object and dhe datapoint type must be set with dataPointType(). Otherwise it will stay unchanged.
*
* @returns true if the value of the group object was changed successfully.
*/
bool tryValue(KNXValue& value);
/**
* returns the currently configured datapoint type.
*/
Dpt dataPointType();
/**
* sets the datapoint type of the group object.
*/
void dataPointType(Dpt value);
#else
/**
* Alternative callback processing: register one global callback for all group object.
* The registered callback will be called if any group object was changed from the bus.
* The callback method has to dispatch to the correct handler for this group object.
*/
static GroupObjectUpdatedHandler classCallback();
static void classCallback(GroupObjectUpdatedHandler handler);
static void processClassCallback(GroupObject& ko);
#endif
private:
// class members
static GroupObjectTableObject* _table;
#ifdef SMALL_GROUPOBJECT
static GroupObjectUpdatedHandler _updateHandlerStatic;
#endif
size_t asapValueSize(uint8_t code) const;
size_t goSize();
uint16_t _asap = 0;
bool _uninitialized : 1;
ComFlag _commFlag : 7;
uint8_t* _data = 0;
uint8_t _dataLength = 0;
#ifndef SMALL_GROUPOBJECT
GroupObjectUpdatedHandler _updateHandler;
Dpt _datapointType;
#endif
};

View File

@ -0,0 +1,131 @@
#include <cstring>
#include "group_object_table_object.h"
#include "group_object.h"
#include "bits.h"
#include "data_property.h"
GroupObjectTableObject::GroupObjectTableObject(Memory& memory)
: TableObject(memory)
{
Property* properties[]
{
new DataProperty(PID_OBJECT_TYPE, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t)OT_GRP_OBJ_TABLE)
};
TableObject::initializeProperties(sizeof(properties), properties);
}
GroupObjectTableObject::~GroupObjectTableObject()
{
freeGroupObjects();
}
uint16_t GroupObjectTableObject::entryCount()
{
if (loadState() != LS_LOADED)
return 0;
return ntohs(_tableData[0]);
}
GroupObject& GroupObjectTableObject::get(uint16_t asap)
{
return _groupObjects[asap - 1];
}
const uint8_t* GroupObjectTableObject::restore(const uint8_t* buffer)
{
buffer = TableObject::restore(buffer);
_tableData = (uint16_t*)data();
initGroupObjects();
return buffer;
}
GroupObject& GroupObjectTableObject::nextUpdatedObject(bool& valid)
{
static uint16_t startIdx = 1;
uint16_t objCount = entryCount();
for (uint16_t asap = startIdx; asap <= objCount; asap++)
{
GroupObject& go = get(asap);
if (go.commFlag() == Updated)
{
go.commFlag(Ok);
startIdx = asap + 1;
valid = true;
return go;
}
}
startIdx = 1;
valid = false;
return get(1);
}
void GroupObjectTableObject::groupObjects(GroupObject* objs, uint16_t size)
{
freeGroupObjects();
_groupObjects = objs;
_groupObjectCount = size;
initGroupObjects();
}
void GroupObjectTableObject::beforeStateChange(LoadState& newState)
{
TableObject::beforeStateChange(newState);
if (newState != LS_LOADED)
return;
_tableData = (uint16_t*)data();
if (!initGroupObjects())
{
newState = LS_ERROR;
TableObject::errorCode(E_SOFTWARE_FAULT);
}
}
bool GroupObjectTableObject::initGroupObjects()
{
if (!_tableData)
return false;
freeGroupObjects();
uint16_t goCount = ntohs(_tableData[0]);
_groupObjects = new GroupObject[goCount];
_groupObjectCount = goCount;
for (uint16_t asap = 1; asap <= goCount; asap++)
{
GroupObject& go = _groupObjects[asap - 1];
go._asap = asap;
go._table = this;
go._dataLength = go.goSize();
size_t sizeInMemory = go.sizeInMemory();
go._data = new uint8_t[sizeInMemory];
memset(go._data, 0, sizeInMemory);
if (go.valueReadOnInit())
go.requestObjectRead();
}
return true;
}
void GroupObjectTableObject::freeGroupObjects()
{
if (_groupObjects)
delete[] _groupObjects;
_groupObjectCount = 0;
_groupObjects = 0;
}

View File

@ -0,0 +1,29 @@
#pragma once
#include "table_object.h"
#include "group_object.h"
class GroupObjectTableObject : public TableObject
{
friend class GroupObject;
public:
GroupObjectTableObject(Memory& memory);
virtual ~GroupObjectTableObject();
uint16_t entryCount();
GroupObject& get(uint16_t asap);
GroupObject& nextUpdatedObject(bool& valid);
void groupObjects(GroupObject* objs, uint16_t size);
const uint8_t* restore(const uint8_t* buffer) override;
protected:
void beforeStateChange(LoadState& newState) override;
private:
void freeGroupObjects();
bool initGroupObjects();
uint16_t* _tableData = 0;
GroupObject* _groupObjects = 0;
uint16_t _groupObjectCount = 0;
};

View File

@ -0,0 +1,232 @@
#include <cstring>
#include "interface_object.h"
#include "data_property.h"
InterfaceObject::~InterfaceObject()
{
if (_properties != nullptr)
delete[] _properties;
}
void InterfaceObject::readPropertyDescription(uint8_t& propertyId, uint8_t& propertyIndex, bool& writeEnable, uint8_t& type, uint16_t& numberOfElements, uint8_t& access)
{
uint8_t count = _propertyCount;
numberOfElements = 0;
if (_properties == nullptr || count == 0)
return;
Property* prop = nullptr;
// from KNX spec. 03.03.07 Application Layer (page 56) - 3.4.3.3 A_PropertyDescription_Read-service
// Summary: either propertyId OR propertyIndex, but not both at the same time
if (propertyId != 0)
{
for (uint8_t i = 0; i < count; i++)
{
Property* p = _properties[i];
if (p->Id() != propertyId)
continue;
prop = p;
propertyIndex = i;
break;
}
}
else
{
// If propertyId is zero, propertyIndex shall be used.
// Response: propertyIndex of received A_PropertyDescription_Read
if (propertyIndex < count)
{
prop = _properties[propertyIndex];
}
}
if (prop != nullptr)
{
propertyId = prop->Id();
writeEnable = prop->WriteEnable();
type = prop->Type();
numberOfElements = prop->MaxElements();
access = prop->Access();
}
}
void InterfaceObject::masterReset(EraseCode eraseCode, uint8_t channel)
{
// every interface object shall implement this
// 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);
if (prop == nullptr)
{
count = 0;
return;
}
count = prop->read(start, count, data);
}
void InterfaceObject::writeProperty(PropertyID id, uint16_t start, uint8_t* data, uint8_t& count)
{
Property* prop = property(id);
if (prop == nullptr)
{
count = 0;
return;
}
count = prop->write(start, count, data);
}
uint8_t InterfaceObject::propertySize(PropertyID id)
{
Property* prop = property(id);
if (prop == nullptr)
{
return 0;
}
return prop->ElementSize();
}
void InterfaceObject::command(PropertyID id, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength)
{
Property* prop = property(id);
if (prop == nullptr)
{
resultLength = 0;
return;
}
prop->command(data, length, resultData, resultLength);
}
void InterfaceObject::state(PropertyID id, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength)
{
Property* prop = property(id);
if (prop == nullptr)
{
resultLength = 0;
return;
}
prop->state(data, length, resultData, resultLength);
}
void InterfaceObject::initializeProperties(size_t propertiesSize, Property** properties)
{
_propertyCount = propertiesSize / sizeof(Property*);
_properties = new Property*[_propertyCount];
memcpy(_properties, properties, propertiesSize);
}
Property* InterfaceObject::property(PropertyID id)
{
for (int i = 0; i < _propertyCount; i++)
if (_properties[i]->Id() == id)
return _properties[i];
return nullptr;
}
uint8_t* InterfaceObject::save(uint8_t* buffer)
{
for (int i = 0; i < _propertyCount; i++)
{
Property* prop = _properties[i];
if (!prop->WriteEnable())
continue;
buffer = prop->save(buffer);
}
return buffer;
}
const uint8_t* InterfaceObject::restore(const uint8_t* buffer)
{
for (int i = 0; i < _propertyCount; i++)
{
Property* prop = _properties[i];
if (!prop->WriteEnable())
continue;
buffer = prop->restore(buffer);
}
return buffer;
}
uint16_t InterfaceObject::saveSize()
{
uint16_t size = 0;
for (int i = 0; i < _propertyCount; i++)
{
Property* prop = _properties[i];
if (!prop->WriteEnable())
continue;
size += prop->saveSize();
}
return size;
}
const Property* InterfaceObject::property(PropertyID id) const
{
for (int i = 0; i < _propertyCount; i++)
if (_properties[i]->Id() == id)
return _properties[i];
return nullptr;
}
const uint8_t* InterfaceObject::propertyData(PropertyID id)
{
DataProperty* prop = (DataProperty*)property(id);
return prop->data();
}
const uint8_t* InterfaceObject::propertyData(PropertyID id, uint16_t elementIndex)
{
DataProperty* prop = (DataProperty*)property(id);
return prop->data(elementIndex);
}

View File

@ -0,0 +1,210 @@
#pragma once
#include <stddef.h>
#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
{
/** Device object. */
OT_DEVICE = 0,
/** Address table object. */
OT_ADDR_TABLE = 1,
/** Association table object. */
OT_ASSOC_TABLE = 2,
/** Application program object. */
OT_APPLICATION_PROG = 3,
/** Interface program object. */
OT_INTERFACE_PROG = 4,
/** KNX - Object Associationtable. */
OT_OJB_ASSOC_TABLE = 5,
/** Router Object */
OT_ROUTER = 6,
/** LTE Address Routing Table Object */
OT_LTE_ADDR_ROUTING_TABLE = 7,
/** cEMI Server Object */
OT_CEMI_SERVER = 8,
/** Group Object Table Object */
OT_GRP_OBJ_TABLE = 9,
/** Polling Master */
OT_POLLING_MASTER = 10,
/** KNXnet/IP Parameter Object */
OT_IP_PARAMETER = 11,
/** Reserved. Shall not be used. */
OT_RESERVED = 12,
/** File Server Object */
OT_FILE_SERVER = 13,
/** Security Interface Object */
OT_SECURITY = 17,
/** RF Medium Object */
OT_RF_MEDIUM = 19,
/** Dummy so this enum is 16bit */
OT_DUMMY = 0xFFFF
};
/**
* This class represents and interface object. See section 4 of @cite knx:3/4/1.
*/
class InterfaceObject : public SaveRestore
{
public:
/**
* 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.
*
* @param id id of the property to read
*
* @param start (for properties with multiple values) at which element should we start
*
* @param[in, out] count how many values should be read. If there is a problem (e.g. property does not exist)
* this value is set to 0.
*
* @param[out] data The requested data of the property.
*/
virtual void readProperty(PropertyID id, uint16_t start, uint8_t& count, uint8_t* data);
/**
* Write property of the interface object. If the interface object does not have the property this
* method does nothing. See section 4.8.4.4 of @cite knx:3/4/1.
*
* @param id id of the property to write
*
* @param start (for properties with multiple values) at which element should we start
*
* @param[in, out] count how many values should be written. If there is a problem (e.g. property does not exist)
* this value is set to 0.
*
* @param[in] data The data that should be written.
*/
virtual void writeProperty(PropertyID id, uint16_t start, uint8_t* data, uint8_t& count);
/**
* Gets the size of of property in bytes.
*
* @param id of the property to get the size of
*
* @returns the size in byte or 0 if the interface object does not have the property
*/
virtual uint8_t propertySize(PropertyID id);
/**
* Call command of a function property of the interface object. Property type must be PDT_FUNCTION
*
* @param id id of the property to call
*
* @param[in] length The size of the data buffer
*
* @param[in] data The argument data for the function
*
* @param[out] resultLength The size of the result data buffer
*
* @param[out] resultData The result data for the function
*/
virtual void command(PropertyID id, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength);
/**
* Get state of a function property of the interface object. Property type must be PDT_FUNCTION
*
* @param id id of the property to call
*
* @param[in] length The size of the data buffer
*
* @param[in] data The argument data for the function
*
* @param[out] resultLength The size of the result data buffer
*
* @param[out] resultData The result data for the function
*/
virtual void state(PropertyID id, uint8_t* data, uint8_t length, uint8_t* resultData, uint8_t& resultLength);
/**
* Read the Description of a property of the interface object. The output parameters are only valid if nuberOfElements is not zero.
*
* @param[in,out] propertyId The id of the property of which to read the description of. If this parameter is not zero
* propertyIndex paramter is ignored as input and the corrrect index of the property is written to it. If this
* parameter is zero the ::PropertyID of the property specified by propertyIndex is written to it.
*
* @param[in,out] propertyIndex The index of the property of the interface object of which to read the description of.
* only used for input if propertyId is not set. Otherwise the index of the property specified by propertyId is written to it.
*
* @param[out] writeEnable Can the property be written to.
*
* @param[out] type the ::PropertyDataType of the property
*
* @param[out] numberOfElements the number of elements of the property. Zero if the interface object does not have the requested property.
*
* @param[out] access the ::AccessLevel necessary to read/write the property.
*/
void readPropertyDescription(uint8_t& propertyId, uint8_t& propertyIndex, bool& writeEnable, uint8_t& type, uint16_t& numberOfElements, uint8_t& access);
// every interface object shall implement this
// However, for the time being we provide an empty default implementation
virtual void masterReset(EraseCode eraseCode, uint8_t channel);
/**
* Gets property with PropertyID id if it exists and nullptr otherwise.
*/
Property* property(PropertyID id);
template <typename T>
T propertyValue(PropertyID id)
{
const Property* prop = property(id);
T value = 0;
prop->read(value);
return value;
}
template <typename T>
void propertyValue(PropertyID id, T value)
{
Property* prop = property(id);
prop->write(value);
}
const uint8_t* propertyData(PropertyID id);
const uint8_t* propertyData(PropertyID id, uint16_t elementIndex);
/**
* Gets const property with PropertyID id if it exists and nullptr otherwise.
*/
const Property* property(PropertyID id) const;
uint8_t* save(uint8_t* buffer) override;
const uint8_t* restore(const uint8_t* buffer) override;
uint16_t saveSize() override;
protected:
/**
* Intializes the Property-array the the supplied values.
*/
virtual void initializeProperties(size_t propertiesSize, Property** properties);
Property** _properties = nullptr;
uint8_t _propertyCount = 0;
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,59 @@
#pragma once
#include "config.h"
#ifdef USE_IP
#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
{
using DataLinkLayer::_deviceObject;
public:
IpDataLinkLayer(DeviceObject& devObj, IpParameterObject& ipParam, NetworkLayerEntity& netLayerEntity,
Platform& platform, 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;
uint8_t _frameCount[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
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 = 0;
#endif
};
#endif

View File

@ -0,0 +1,48 @@
#include "ip_host_protocol_address_information.h"
#include "bits.h"
#ifdef USE_IP
IpHostProtocolAddressInformation::IpHostProtocolAddressInformation(uint8_t* data)
: _data(data)
{}
uint8_t IpHostProtocolAddressInformation::length() const
{
return *_data;
}
void IpHostProtocolAddressInformation::length(uint8_t value)
{
*_data = value;
}
HostProtocolCode IpHostProtocolAddressInformation::code() const
{
return (HostProtocolCode)_data[1];
}
void IpHostProtocolAddressInformation::code(HostProtocolCode value)
{
_data[1] = value;
}
uint32_t IpHostProtocolAddressInformation::ipAddress() const
{
return getInt(_data + 2);
}
void IpHostProtocolAddressInformation::ipAddress(uint32_t value)
{
pushInt(value, _data + 2);
}
uint16_t IpHostProtocolAddressInformation::ipPortNumber() const
{
return getWord(_data + 6);
}
void IpHostProtocolAddressInformation::ipPortNumber(uint16_t value)
{
pushWord(value, _data + 6);
}
#endif

View File

@ -0,0 +1,33 @@
#pragma once
#include <cstdint>
#include "config.h"
enum HostProtocolCode : uint8_t
{
IPV4_UDP = 1,
IPV4_TCP = 2
};
#ifdef USE_IP
#define LEN_IPHPAI 8
#define LEN_CRD 4
class IpHostProtocolAddressInformation
{
public:
IpHostProtocolAddressInformation(uint8_t* data);
uint8_t length() const;
void length(uint8_t value);
HostProtocolCode code() const;
void code(HostProtocolCode value);
uint32_t ipAddress() const;
void ipAddress(uint32_t value);
uint16_t ipPortNumber() const;
void ipPortNumber(uint16_t value);
private:
uint8_t* _data;
};
#endif

View File

@ -0,0 +1,143 @@
#include "ip_parameter_object.h"
#ifdef USE_IP
#include "device_object.h"
#include "platform.h"
#include "bits.h"
#include "data_property.h"
#include "callback_property.h"
// 224.0.23.12
#define DEFAULT_MULTICAST_ADDR ((uint32_t)0xE000170C)
IpParameterObject::IpParameterObject(DeviceObject& deviceObject, Platform& platform): _deviceObject(deviceObject),
_platform(platform)
{
Property* properties[] =
{
new DataProperty(PID_OBJECT_TYPE, false, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv0, (uint16_t)OT_IP_PARAMETER),
new DataProperty(PID_PROJECT_INSTALLATION_ID, true, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv3),
new CallbackProperty<IpParameterObject>(this, PID_KNX_INDIVIDUAL_ADDRESS, true, PDT_UNSIGNED_INT, 1, ReadLv3 | WriteLv3,
[](IpParameterObject * io, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t
{
if (start == 0)
{
uint16_t currentNoOfElements = 1;
pushWord(currentNoOfElements, data);
return 1;
}
// TODO: get property of deviceobject and use it
pushWord(io->_deviceObject.individualAddress(), data);
return 1;
},
[](IpParameterObject * io, uint16_t start, uint8_t count, const uint8_t* data) -> uint8_t
{
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
new CallbackProperty<IpParameterObject>(this, PID_CURRENT_IP_ADDRESS, false, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv0,
[](IpParameterObject * io, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t
{
if (start == 0)
{
uint16_t currentNoOfElements = 1;
pushWord(currentNoOfElements, data);
return 1;
}
pushInt(htonl(io->_platform.currentIpAddress()), data);
return 1;
}),
new CallbackProperty<IpParameterObject>(this, PID_CURRENT_SUBNET_MASK, false, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv0,
[](IpParameterObject * io, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t
{
if (start == 0)
{
uint16_t currentNoOfElements = 1;
pushWord(currentNoOfElements, data);
return 1;
}
pushInt(htonl(io->_platform.currentSubnetMask()), data);
return 1;
}),
new CallbackProperty<IpParameterObject>(this, PID_CURRENT_DEFAULT_GATEWAY, false, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv0,
[](IpParameterObject * io, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t
{
if (start == 0)
{
uint16_t currentNoOfElements = 1;
pushWord(currentNoOfElements, data);
return 1;
}
pushInt(htonl(io->_platform.currentDefaultGateway()), data);
return 1;
}),
new DataProperty(PID_IP_ADDRESS, true, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv3),
new DataProperty(PID_SUBNET_MASK, true, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv3),
new DataProperty(PID_DEFAULT_GATEWAY, true, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv3),
new CallbackProperty<IpParameterObject>(this, PID_MAC_ADDRESS, false, PDT_GENERIC_06, 1, ReadLv3 | WriteLv0,
[](IpParameterObject * io, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t
{
if (start == 0)
{
uint16_t currentNoOfElements = 1;
pushWord(currentNoOfElements, data);
return 1;
}
io->_platform.macAddress(data);
return 1;
}),
new CallbackProperty<IpParameterObject>(this, PID_SYSTEM_SETUP_MULTICAST_ADDRESS, false, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv0,
[](IpParameterObject * io, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t
{
if (start == 0)
{
uint16_t currentNoOfElements = 1;
pushWord(currentNoOfElements, data);
return 1;
}
pushInt(DEFAULT_MULTICAST_ADDR, data);
return 1;
}),
new DataProperty(PID_ROUTING_MULTICAST_ADDRESS, true, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv3, DEFAULT_MULTICAST_ADDR),
new DataProperty(PID_TTL, true, PDT_UNSIGNED_CHAR, 1, ReadLv3 | WriteLv3, (uint8_t)16),
new CallbackProperty<IpParameterObject>(this, PID_KNXNETIP_DEVICE_CAPABILITIES, false, PDT_BITSET16, 1, ReadLv3 | WriteLv0,
[](IpParameterObject * io, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t
{
if (start == 0)
{
uint16_t currentNoOfElements = 1;
pushWord(currentNoOfElements, data);
return 1;
}
pushWord(0x1, data);
return 1;
}),
new DataProperty(PID_FRIENDLY_NAME, true, PDT_UNSIGNED_CHAR, 30, ReadLv3 | WriteLv3)
};
initializeProperties(sizeof(properties), properties);
}
uint16_t* IpParameterObject::additionalIndivualAddresses(uint8_t& numAddresses)
{
#ifdef KNX_TUNNELING
numAddresses = KNX_TUNNELING;
#else
numAddresses = 0;
#endif
return (uint16_t*) propertyData(PID_ADDITIONAL_INDIVIDUAL_ADDRESSES);
}
#endif

View File

@ -0,0 +1,20 @@
#pragma once
#include "config.h"
#ifdef USE_IP
#include "interface_object.h"
#include "device_object.h"
#include "platform.h"
#define KNXIP_MULTICAST_PORT 3671
class IpParameterObject : public InterfaceObject
{
public:
IpParameterObject(DeviceObject& deviceObject, Platform& platform);
uint16_t* additionalIndivualAddresses(uint8_t& numAddresses);
private:
DeviceObject& _deviceObject;
Platform& _platform;
};
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,46 @@
#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);
if(type == 4) // only fill address when it is a TunnelConnectionResponse
_crd.address(address);
}
KnxIpConnectResponse::KnxIpConnectResponse(uint8_t channel, uint8_t errorCode)
: KnxIpFrame(LEN_KNXIP_HEADER + 1 /*Channel*/ + 1 /*Status*/),
_controlEndpoint(nullptr),
_crd(nullptr)
{
serviceTypeIdentifier(ConnectResponse);
_data[LEN_KNXIP_HEADER] = channel;
_data[LEN_KNXIP_HEADER + 1] = errorCode;
}
IpHostProtocolAddressInformation& KnxIpConnectResponse::controlEndpoint()
{
return _controlEndpoint;
}
KnxIpCRD& KnxIpConnectResponse::crd()
{
return _crd;
}
#endif

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -0,0 +1,102 @@
#include "knx_ip_device_information_dib.h"
#include "bits.h"
#ifdef USE_IP
KnxIpDeviceInformationDIB::KnxIpDeviceInformationDIB(uint8_t* data) : KnxIpDIB(data)
{}
uint8_t KnxIpDeviceInformationDIB::medium() const
{
return _data[2];
}
void KnxIpDeviceInformationDIB::medium(uint8_t value)
{
_data[2] = value;
}
uint8_t KnxIpDeviceInformationDIB::status() const
{
return _data[3];
}
void KnxIpDeviceInformationDIB::status(uint8_t value)
{
_data[3] = value;
}
uint16_t KnxIpDeviceInformationDIB::individualAddress() const
{
return getWord(_data + 4);
}
void KnxIpDeviceInformationDIB::individualAddress(uint16_t value)
{
pushWord(value, _data + 4);
}
uint16_t KnxIpDeviceInformationDIB::projectInstallationIdentifier() const
{
return getWord(_data + 6);
}
void KnxIpDeviceInformationDIB::projectInstallationIdentifier(uint16_t value)
{
pushWord(value, _data + 6);
}
const uint8_t* KnxIpDeviceInformationDIB::serialNumber() const
{
return _data + 8;
}
void KnxIpDeviceInformationDIB::serialNumber(const uint8_t* value)
{
pushByteArray(value, LEN_SERIAL_NUMBER, _data + 8);
}
uint32_t KnxIpDeviceInformationDIB::routingMulticastAddress() const
{
return getInt(_data + 14);
}
void KnxIpDeviceInformationDIB::routingMulticastAddress(uint32_t value)
{
pushInt(value, _data + 14);
}
const uint8_t* KnxIpDeviceInformationDIB::macAddress() const
{
return _data + 18;
}
void KnxIpDeviceInformationDIB::macAddress(const uint8_t* value)
{
pushByteArray(value, LEN_MAC_ADDRESS, _data + 18);
}
const uint8_t* KnxIpDeviceInformationDIB::friendlyName() const
{
return _data + 24;
}
void KnxIpDeviceInformationDIB::friendlyName(const uint8_t* value)
{
pushByteArray(value, LEN_FRIENDLY_NAME, _data + 24);
}
#endif

View File

@ -0,0 +1,32 @@
#pragma once
#include "knx_ip_dib.h"
#ifdef USE_IP
#define LEN_DEVICE_INFORMATION_DIB 54
#define LEN_SERIAL_NUMBER 6
#define LEN_MAC_ADDRESS 6
#define LEN_FRIENDLY_NAME 30
class KnxIpDeviceInformationDIB : public KnxIpDIB
{
public:
KnxIpDeviceInformationDIB(uint8_t* data);
uint8_t medium() const;
void medium(uint8_t value);
uint8_t status() const;
void status(uint8_t value);
uint16_t individualAddress() const;
void individualAddress(uint16_t value);
uint16_t projectInstallationIdentifier() const;
void projectInstallationIdentifier(uint16_t value);
const uint8_t* serialNumber() const;
void serialNumber(const uint8_t* value);
uint32_t routingMulticastAddress() const;
void routingMulticastAddress(uint32_t value);
const uint8_t* macAddress() const;
void macAddress(const uint8_t* value);
const uint8_t* friendlyName() const;
void friendlyName(const uint8_t* value);
};
#endif

View File

@ -0,0 +1,28 @@
#include "knx_ip_dib.h"
#ifdef USE_IP
KnxIpDIB::KnxIpDIB(uint8_t* data) : _data(data)
{}
KnxIpDIB::~KnxIpDIB()
{}
uint8_t KnxIpDIB::length() const
{
return *_data;
}
void KnxIpDIB::length(uint8_t value)
{
*_data = value;
}
DescriptionTypeCode KnxIpDIB::code() const
{
return (DescriptionTypeCode)_data[1];
}
void KnxIpDIB::code(DescriptionTypeCode value)
{
_data[1] = value;
}
#endif

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