First Try
This commit is contained in:
parent
efde86778b
commit
53e123dc8a
@ -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)
|
||||
|
||||
11
components/knx/CMakeLists.txt
Normal file
11
components/knx/CMakeLists.txt
Normal 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)
|
||||
315
components/knx/src/arduino_platform.cpp
Normal file
315
components/knx/src/arduino_platform.cpp
Normal 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
|
||||
47
components/knx/src/arduino_platform.h
Normal file
47
components/knx/src/arduino_platform.h
Normal 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
|
||||
587
components/knx/src/cc1310_platform.cpp
Normal file
587
components/knx/src/cc1310_platform.cpp
Normal 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
|
||||
27
components/knx/src/cc1310_platform.h
Normal file
27
components/knx/src/cc1310_platform.h
Normal 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
|
||||
443
components/knx/src/esp32_idf_platform.cpp
Normal file
443
components/knx/src/esp32_idf_platform.cpp
Normal 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
|
||||
89
components/knx/src/esp32_idf_platform.h
Normal file
89
components/knx/src/esp32_idf_platform.h
Normal 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
|
||||
184
components/knx/src/esp32_platform.cpp
Normal file
184
components/knx/src/esp32_platform.cpp
Normal 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
|
||||
54
components/knx/src/esp32_platform.h
Normal file
54
components/knx/src/esp32_platform.h
Normal 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
|
||||
135
components/knx/src/esp_platform.cpp
Normal file
135
components/knx/src/esp_platform.cpp
Normal 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
|
||||
43
components/knx/src/esp_platform.h
Normal file
43
components/knx/src/esp_platform.h
Normal 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
248
components/knx/src/knx.h
Normal 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"
|
||||
96
components/knx/src/knx/address_table_object.cpp
Normal file
96
components/knx/src/knx/address_table_object.cpp
Normal 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();
|
||||
}
|
||||
57
components/knx/src/knx/address_table_object.h
Normal file
57
components/knx/src/knx/address_table_object.h
Normal 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;
|
||||
};
|
||||
606
components/knx/src/knx/aes.c
Normal file
606
components/knx/src/knx/aes.c
Normal 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)
|
||||
|
||||
90
components/knx/src/knx/aes.h
Normal file
90
components/knx/src/knx/aes.h
Normal 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_
|
||||
12
components/knx/src/knx/aes.hpp
Normal file
12
components/knx/src/knx/aes.hpp
Normal 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_
|
||||
59
components/knx/src/knx/apdu.cpp
Normal file
59
components/knx/src/knx/apdu.cpp
Normal 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();
|
||||
}
|
||||
53
components/knx/src/knx/apdu.h
Normal file
53
components/knx/src/knx/apdu.h
Normal 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;
|
||||
};
|
||||
1546
components/knx/src/knx/application_layer.cpp
Normal file
1546
components/knx/src/knx/application_layer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
228
components/knx/src/knx/application_layer.h
Normal file
228
components/knx/src/knx/application_layer.h
Normal 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;
|
||||
};
|
||||
99
components/knx/src/knx/application_program_object.cpp
Normal file
99
components/knx/src/knx/application_program_object.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
18
components/knx/src/knx/application_program_object.h
Normal file
18
components/knx/src/knx/application_program_object.h
Normal 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);
|
||||
};
|
||||
174
components/knx/src/knx/association_table_object.cpp
Normal file
174
components/knx/src/knx/association_table_object.cpp
Normal 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;
|
||||
}
|
||||
25
components/knx/src/knx/association_table_object.h
Normal file
25
components/knx/src/knx/association_table_object.h
Normal 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;
|
||||
};
|
||||
392
components/knx/src/knx/bau.cpp
Normal file
392
components/knx/src/knx/bau.cpp
Normal 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;
|
||||
}
|
||||
184
components/knx/src/knx/bau.h
Normal file
184
components/knx/src/knx/bau.h
Normal 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();
|
||||
};
|
||||
178
components/knx/src/knx/bau07B0.cpp
Normal file
178
components/knx/src/knx/bau07B0.cpp
Normal 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
|
||||
34
components/knx/src/knx/bau07B0.h
Normal file
34
components/knx/src/knx/bau07B0.h
Normal 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
|
||||
262
components/knx/src/knx/bau091A.cpp
Normal file
262
components/knx/src/knx/bau091A.cpp
Normal 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
|
||||
42
components/knx/src/knx/bau091A.h
Normal file
42
components/knx/src/knx/bau091A.h
Normal 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
|
||||
207
components/knx/src/knx/bau27B0.cpp
Normal file
207
components/knx/src/knx/bau27B0.cpp
Normal 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
|
||||
47
components/knx/src/knx/bau27B0.h
Normal file
47
components/knx/src/knx/bau27B0.h
Normal 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
|
||||
181
components/knx/src/knx/bau2920.cpp
Normal file
181
components/knx/src/knx/bau2920.cpp
Normal 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
|
||||
43
components/knx/src/knx/bau2920.h
Normal file
43
components/knx/src/knx/bau2920.h
Normal 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
|
||||
169
components/knx/src/knx/bau57B0.cpp
Normal file
169
components/knx/src/knx/bau57B0.cpp
Normal 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
|
||||
33
components/knx/src/knx/bau57B0.h
Normal file
33
components/knx/src/knx/bau57B0.h
Normal 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
|
||||
812
components/knx/src/knx/bau_systemB.cpp
Normal file
812
components/knx/src/knx/bau_systemB.cpp
Normal 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;
|
||||
}
|
||||
133
components/knx/src/knx/bau_systemB.h
Normal file
133
components/knx/src/knx/bau_systemB.h
Normal 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;
|
||||
};
|
||||
60
components/knx/src/knx/bau_systemB_coupler.cpp
Normal file
60
components/knx/src/knx/bau_systemB_coupler.cpp
Normal 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
|
||||
}
|
||||
40
components/knx/src/knx/bau_systemB_coupler.h
Normal file
40
components/knx/src/knx/bau_systemB_coupler.h
Normal 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;
|
||||
};
|
||||
262
components/knx/src/knx/bau_systemB_device.cpp
Normal file
262
components/knx/src/knx/bau_systemB_device.cpp
Normal 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);
|
||||
}
|
||||
57
components/knx/src/knx/bau_systemB_device.h
Normal file
57
components/knx/src/knx/bau_systemB_device.h
Normal 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;
|
||||
};
|
||||
364
components/knx/src/knx/bits.cpp
Normal file
364
components/knx/src/knx/bits.cpp
Normal 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
|
||||
161
components/knx/src/knx/bits.h
Normal file
161
components/knx/src/knx/bits.h
Normal 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
|
||||
39
components/knx/src/knx/callback_property.h
Normal file
39
components/knx/src/knx/callback_property.h
Normal 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;
|
||||
};
|
||||
403
components/knx/src/knx/cemi_frame.cpp
Normal file
403
components/knx/src/knx/cemi_frame.cpp
Normal 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;
|
||||
}
|
||||
94
components/knx/src/knx/cemi_frame.h
Normal file
94
components/knx/src/knx/cemi_frame.h
Normal 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;
|
||||
};
|
||||
443
components/knx/src/knx/cemi_server.cpp
Normal file
443
components/knx/src/knx/cemi_server.cpp
Normal 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
|
||||
68
components/knx/src/knx/cemi_server.h
Normal file
68
components/knx/src/knx/cemi_server.h
Normal 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
|
||||
61
components/knx/src/knx/cemi_server_object.cpp
Normal file
61
components/knx/src/knx/cemi_server_object.cpp
Normal 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
|
||||
|
||||
17
components/knx/src/knx/cemi_server_object.h
Normal file
17
components/knx/src/knx/cemi_server_object.h
Normal 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
|
||||
84
components/knx/src/knx/config.h
Normal file
84
components/knx/src/knx/config.h
Normal 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
|
||||
|
||||
309
components/knx/src/knx/data_link_layer.cpp
Normal file
309
components/knx/src/knx/data_link_layer.cpp
Normal 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
|
||||
|
||||
77
components/knx/src/knx/data_link_layer.h
Normal file
77
components/knx/src/knx/data_link_layer.h
Normal 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
|
||||
};
|
||||
168
components/knx/src/knx/data_property.cpp
Normal file
168
components/knx/src/knx/data_property.cpp
Normal 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;
|
||||
}
|
||||
25
components/knx/src/knx/data_property.h
Normal file
25
components/knx/src/knx/data_property.h
Normal 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;
|
||||
};
|
||||
64
components/knx/src/knx/datapoint_types.cpp
Normal file
64
components/knx/src/knx/datapoint_types.cpp
Normal 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;
|
||||
}
|
||||
37
components/knx/src/knx/datapoint_types.h
Normal file
37
components/knx/src/knx/datapoint_types.h
Normal 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);
|
||||
294
components/knx/src/knx/device_object.cpp
Normal file
294
components/knx/src/knx/device_object.cpp
Normal 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;
|
||||
}
|
||||
51
components/knx/src/knx/device_object.h
Normal file
51
components/knx/src/knx/device_object.h
Normal 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
|
||||
};
|
||||
23
components/knx/src/knx/dpt.cpp
Normal file
23
components/knx/src/knx/dpt.cpp
Normal 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);
|
||||
}
|
||||
373
components/knx/src/knx/dpt.h
Normal file
373
components/knx/src/knx/dpt.h
Normal 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;
|
||||
};
|
||||
2008
components/knx/src/knx/dptconvert.cpp
Normal file
2008
components/knx/src/knx/dptconvert.cpp
Normal file
File diff suppressed because it is too large
Load Diff
149
components/knx/src/knx/dptconvert.h
Normal file
149
components/knx/src/knx/dptconvert.h
Normal 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);
|
||||
53
components/knx/src/knx/function_property.h
Normal file
53
components/knx/src/knx/function_property.h
Normal 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;
|
||||
};
|
||||
356
components/knx/src/knx/group_object.cpp
Normal file
356
components/knx/src/knx/group_object.cpp
Normal 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;
|
||||
}
|
||||
287
components/knx/src/knx/group_object.h
Normal file
287
components/knx/src/knx/group_object.h
Normal 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
|
||||
};
|
||||
131
components/knx/src/knx/group_object_table_object.cpp
Normal file
131
components/knx/src/knx/group_object_table_object.cpp
Normal 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;
|
||||
}
|
||||
29
components/knx/src/knx/group_object_table_object.h
Normal file
29
components/knx/src/knx/group_object_table_object.h
Normal 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;
|
||||
};
|
||||
232
components/knx/src/knx/interface_object.cpp
Normal file
232
components/knx/src/knx/interface_object.cpp
Normal 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);
|
||||
}
|
||||
210
components/knx/src/knx/interface_object.h
Normal file
210
components/knx/src/knx/interface_object.h
Normal 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;
|
||||
};
|
||||
1191
components/knx/src/knx/ip_data_link_layer.cpp
Normal file
1191
components/knx/src/knx/ip_data_link_layer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
59
components/knx/src/knx/ip_data_link_layer.h
Normal file
59
components/knx/src/knx/ip_data_link_layer.h
Normal 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
|
||||
@ -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
|
||||
@ -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
|
||||
143
components/knx/src/knx/ip_parameter_object.cpp
Normal file
143
components/knx/src/knx/ip_parameter_object.cpp
Normal 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
|
||||
20
components/knx/src/knx/ip_parameter_object.h
Normal file
20
components/knx/src/knx/ip_parameter_object.h
Normal 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
|
||||
48
components/knx/src/knx/knx_ip_ch.cpp
Normal file
48
components/knx/src/knx/knx_ip_ch.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include "knx_ip_ch.h"
|
||||
#ifdef USE_IP
|
||||
KnxIpCH::KnxIpCH(uint8_t* data) : _data(data)
|
||||
{}
|
||||
|
||||
KnxIpCH::~KnxIpCH()
|
||||
{}
|
||||
|
||||
uint8_t KnxIpCH::length() const
|
||||
{
|
||||
return *_data;
|
||||
}
|
||||
|
||||
void KnxIpCH::length(uint8_t value)
|
||||
{
|
||||
*_data = value;
|
||||
}
|
||||
|
||||
void KnxIpCH::channelId(uint8_t value)
|
||||
{
|
||||
_data[1] = value;
|
||||
}
|
||||
|
||||
uint8_t KnxIpCH::channelId() const
|
||||
{
|
||||
return _data[1];
|
||||
}
|
||||
|
||||
void KnxIpCH::sequenceCounter(uint8_t value)
|
||||
{
|
||||
_data[2] = value;
|
||||
}
|
||||
|
||||
uint8_t KnxIpCH::sequenceCounter() const
|
||||
{
|
||||
return _data[2];
|
||||
}
|
||||
|
||||
void KnxIpCH::status(uint8_t value)
|
||||
{
|
||||
_data[3] = value;
|
||||
}
|
||||
|
||||
uint8_t KnxIpCH::status() const
|
||||
{
|
||||
return _data[3];
|
||||
}
|
||||
#endif
|
||||
28
components/knx/src/knx/knx_ip_ch.h
Normal file
28
components/knx/src/knx/knx_ip_ch.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "config.h"
|
||||
|
||||
#ifdef USE_IP
|
||||
|
||||
#define LEN_CH 4
|
||||
|
||||
// Connection Header
|
||||
class KnxIpCH
|
||||
{
|
||||
public:
|
||||
KnxIpCH(uint8_t* data);
|
||||
virtual ~KnxIpCH();
|
||||
void channelId(uint8_t channelId);
|
||||
uint8_t channelId() const;
|
||||
void sequenceCounter(uint8_t sequenceCounter);
|
||||
uint8_t sequenceCounter() const;
|
||||
void status(uint8_t status);
|
||||
uint8_t status() const;
|
||||
void length(uint8_t value);
|
||||
uint8_t length() const;
|
||||
|
||||
protected:
|
||||
uint8_t* _data = 0;
|
||||
};
|
||||
#endif
|
||||
95
components/knx/src/knx/knx_ip_config_dib.cpp
Normal file
95
components/knx/src/knx/knx_ip_config_dib.cpp
Normal 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
|
||||
28
components/knx/src/knx/knx_ip_config_dib.h
Normal file
28
components/knx/src/knx/knx_ip_config_dib.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
#include "knx_ip_dib.h"
|
||||
#include "bits.h"
|
||||
|
||||
#ifdef USE_IP
|
||||
#define LEN_IP_CONFIG_DIB 16
|
||||
#define LEN_IP_CURRENT_CONFIG_DIB 20
|
||||
|
||||
class KnxIpConfigDIB : public KnxIpDIB
|
||||
{
|
||||
public:
|
||||
KnxIpConfigDIB(uint8_t* data, bool isCurrent = false);
|
||||
uint32_t address();
|
||||
void address(uint32_t addr);
|
||||
uint32_t subnet();
|
||||
void subnet(uint32_t addr);
|
||||
uint32_t gateway();
|
||||
void gateway(uint32_t addr);
|
||||
uint32_t dhcp();
|
||||
void dhcp(uint32_t addr);
|
||||
uint8_t info1();
|
||||
void info1(uint8_t addr);
|
||||
uint8_t info2();
|
||||
void info2(uint8_t addr);
|
||||
private:
|
||||
bool _isCurrent = false;
|
||||
};
|
||||
#endif
|
||||
17
components/knx/src/knx/knx_ip_config_request.cpp
Normal file
17
components/knx/src/knx/knx_ip_config_request.cpp
Normal file
@ -0,0 +1,17 @@
|
||||
#include "knx_ip_config_request.h"
|
||||
#ifdef USE_IP
|
||||
KnxIpConfigRequest::KnxIpConfigRequest(uint8_t* data, uint16_t length)
|
||||
: KnxIpFrame(data, length), _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
|
||||
17
components/knx/src/knx/knx_ip_config_request.h
Normal file
17
components/knx/src/knx/knx_ip_config_request.h
Normal file
@ -0,0 +1,17 @@
|
||||
#pragma once
|
||||
|
||||
#include "knx_ip_frame.h"
|
||||
#include "knx_ip_ch.h"
|
||||
#include "ip_host_protocol_address_information.h"
|
||||
#ifdef USE_IP
|
||||
class KnxIpConfigRequest : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpConfigRequest(uint8_t* data, uint16_t length);
|
||||
CemiFrame& frame();
|
||||
KnxIpCH& connectionHeader();
|
||||
private:
|
||||
CemiFrame _frame;
|
||||
KnxIpCH _ch;
|
||||
};
|
||||
#endif
|
||||
21
components/knx/src/knx/knx_ip_connect_request.cpp
Normal file
21
components/knx/src/knx/knx_ip_connect_request.cpp
Normal file
@ -0,0 +1,21 @@
|
||||
#include "knx_ip_connect_request.h"
|
||||
#ifdef USE_IP
|
||||
KnxIpConnectRequest::KnxIpConnectRequest(uint8_t* data, uint16_t length)
|
||||
: KnxIpFrame(data, length), _hpaiCtrl(data + LEN_KNXIP_HEADER), _hpaiData(data + LEN_KNXIP_HEADER + LEN_IPHPAI), _cri(data + LEN_KNXIP_HEADER + 2 * LEN_IPHPAI)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
IpHostProtocolAddressInformation& KnxIpConnectRequest::hpaiCtrl()
|
||||
{
|
||||
return _hpaiCtrl;
|
||||
}
|
||||
IpHostProtocolAddressInformation& KnxIpConnectRequest::hpaiData()
|
||||
{
|
||||
return _hpaiData;
|
||||
}
|
||||
KnxIpCRI& KnxIpConnectRequest::cri()
|
||||
{
|
||||
return _cri;
|
||||
}
|
||||
#endif
|
||||
19
components/knx/src/knx/knx_ip_connect_request.h
Normal file
19
components/knx/src/knx/knx_ip_connect_request.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "knx_ip_frame.h"
|
||||
#include "knx_ip_cri.h"
|
||||
#include "ip_host_protocol_address_information.h"
|
||||
#ifdef USE_IP
|
||||
class KnxIpConnectRequest : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpConnectRequest(uint8_t* data, uint16_t length);
|
||||
IpHostProtocolAddressInformation& hpaiCtrl();
|
||||
IpHostProtocolAddressInformation& hpaiData();
|
||||
KnxIpCRI& cri();
|
||||
private:
|
||||
IpHostProtocolAddressInformation _hpaiCtrl;
|
||||
IpHostProtocolAddressInformation _hpaiData;
|
||||
KnxIpCRI _cri;
|
||||
};
|
||||
#endif
|
||||
46
components/knx/src/knx/knx_ip_connect_response.cpp
Normal file
46
components/knx/src/knx/knx_ip_connect_response.cpp
Normal 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
|
||||
45
components/knx/src/knx/knx_ip_connect_response.h
Normal file
45
components/knx/src/knx/knx_ip_connect_response.h
Normal file
@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "knx_ip_frame.h"
|
||||
#include "knx_ip_crd.h"
|
||||
#include "ip_host_protocol_address_information.h"
|
||||
#include "knx_ip_device_information_dib.h"
|
||||
#include "knx_ip_supported_service_dib.h"
|
||||
#include "ip_parameter_object.h"
|
||||
#ifdef USE_IP
|
||||
|
||||
enum KnxIpConnectionRequestErrorCodes
|
||||
{
|
||||
E_NO_ERROR = 0,
|
||||
|
||||
E_HOST_PROTOCOL_TYPE = 0x01,
|
||||
E_VERSION_NOT_SUPPORTED = 0x02,
|
||||
E_SEQUENCE_NUMBER = 0x04,
|
||||
|
||||
E_ERROR = 0x0F,
|
||||
|
||||
E_CONNECTION_ID = 0x21,
|
||||
E_CONNECTION_TYPE = 0x22,
|
||||
E_CONNECTION_OPTION = 0x23,
|
||||
E_NO_MORE_CONNECTIONS = 0x24,
|
||||
E_DATA_CONNECTION = 0x26,
|
||||
E_KNX_CONNECTION = 0x27,
|
||||
E_AUTHORISATION_ERROR = 0x28,
|
||||
E_TUNNELING_LAYER = 0x29,
|
||||
E_NO_TUNNELLING_ADDRESS = 0x2D,
|
||||
E_CONNECTION_IN_USE = 0x2E
|
||||
};
|
||||
|
||||
class KnxIpConnectResponse : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpConnectResponse(IpParameterObject& parameters, uint16_t address, uint16_t port, uint8_t channel, uint8_t type);
|
||||
KnxIpConnectResponse(uint8_t channel, uint8_t errorCode);
|
||||
IpHostProtocolAddressInformation& controlEndpoint();
|
||||
KnxIpCRD& crd();
|
||||
private:
|
||||
IpHostProtocolAddressInformation _controlEndpoint;
|
||||
KnxIpCRD _crd;
|
||||
};
|
||||
|
||||
#endif
|
||||
41
components/knx/src/knx/knx_ip_crd.cpp
Normal file
41
components/knx/src/knx/knx_ip_crd.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
#include "knx_ip_crd.h"
|
||||
#ifdef USE_IP
|
||||
KnxIpCRD::KnxIpCRD(uint8_t* data) : _data(data)
|
||||
{}
|
||||
|
||||
KnxIpCRD::~KnxIpCRD()
|
||||
{}
|
||||
|
||||
uint8_t KnxIpCRD::length() const
|
||||
{
|
||||
return *_data;
|
||||
}
|
||||
|
||||
void KnxIpCRD::length(uint8_t value)
|
||||
{
|
||||
*_data = value;
|
||||
}
|
||||
|
||||
uint8_t KnxIpCRD::type() const
|
||||
{
|
||||
return _data[1];
|
||||
}
|
||||
|
||||
void KnxIpCRD::type(uint8_t value)
|
||||
{
|
||||
_data[1] = value;
|
||||
}
|
||||
|
||||
uint16_t KnxIpCRD::address() const
|
||||
{
|
||||
uint16_t addr = _data[3];
|
||||
addr |= _data[2] << 8;
|
||||
return addr;
|
||||
}
|
||||
|
||||
void KnxIpCRD::address(uint16_t value)
|
||||
{
|
||||
_data[2] = value >> 8;
|
||||
_data[3] = value & 0xFF;
|
||||
}
|
||||
#endif
|
||||
23
components/knx/src/knx/knx_ip_crd.h
Normal file
23
components/knx/src/knx/knx_ip_crd.h
Normal file
@ -0,0 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "config.h"
|
||||
|
||||
#ifdef USE_IP
|
||||
|
||||
class KnxIpCRD
|
||||
{
|
||||
public:
|
||||
KnxIpCRD(uint8_t* data);
|
||||
virtual ~KnxIpCRD();
|
||||
void address(uint16_t addr);
|
||||
uint16_t address() const;
|
||||
void type(uint8_t addr);
|
||||
uint8_t type() const;
|
||||
uint8_t length() const;
|
||||
void length(uint8_t value);
|
||||
|
||||
protected:
|
||||
uint8_t* _data = 0;
|
||||
};
|
||||
#endif
|
||||
38
components/knx/src/knx/knx_ip_cri.cpp
Normal file
38
components/knx/src/knx/knx_ip_cri.cpp
Normal file
@ -0,0 +1,38 @@
|
||||
#include "knx_ip_cri.h"
|
||||
#ifdef USE_IP
|
||||
KnxIpCRI::KnxIpCRI(uint8_t* data) : _data(data)
|
||||
{}
|
||||
|
||||
KnxIpCRI::~KnxIpCRI()
|
||||
{}
|
||||
|
||||
uint8_t KnxIpCRI::length() const
|
||||
{
|
||||
return *_data;
|
||||
}
|
||||
|
||||
void KnxIpCRI::length(uint8_t value)
|
||||
{
|
||||
*_data = value;
|
||||
}
|
||||
|
||||
ConnectionType KnxIpCRI::type() const
|
||||
{
|
||||
return (ConnectionType)_data[1];
|
||||
}
|
||||
|
||||
void KnxIpCRI::type(ConnectionType value)
|
||||
{
|
||||
_data[1] = value;
|
||||
}
|
||||
|
||||
uint8_t KnxIpCRI::layer() const
|
||||
{
|
||||
return _data[2];
|
||||
}
|
||||
|
||||
void KnxIpCRI::layer(uint8_t value)
|
||||
{
|
||||
_data[2] = value;
|
||||
}
|
||||
#endif
|
||||
36
components/knx/src/knx/knx_ip_cri.h
Normal file
36
components/knx/src/knx/knx_ip_cri.h
Normal file
@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include "config.h"
|
||||
|
||||
#ifdef USE_IP
|
||||
|
||||
#define LEN_CRI 4
|
||||
|
||||
//TODO vervollständigen
|
||||
enum ConnectionType : uint8_t
|
||||
{
|
||||
DEVICE_MGMT_CONNECTION = 3,
|
||||
TUNNEL_CONNECTION = 4,
|
||||
REMLOG_CONNECTION = 6,
|
||||
REMCONF_CONNECTION = 7,
|
||||
OBJSVR_CONNECTION = 8
|
||||
};
|
||||
|
||||
// Connection Request Information
|
||||
class KnxIpCRI
|
||||
{
|
||||
public:
|
||||
KnxIpCRI(uint8_t* data);
|
||||
virtual ~KnxIpCRI();
|
||||
ConnectionType type() const;
|
||||
void type(ConnectionType value);
|
||||
void layer(uint8_t layer);
|
||||
uint8_t layer() const;
|
||||
uint8_t length() const;
|
||||
void length(uint8_t value);
|
||||
|
||||
protected:
|
||||
uint8_t* _data = 0;
|
||||
};
|
||||
#endif
|
||||
13
components/knx/src/knx/knx_ip_description_request.cpp
Normal file
13
components/knx/src/knx/knx_ip_description_request.cpp
Normal file
@ -0,0 +1,13 @@
|
||||
#include "knx_ip_description_request.h"
|
||||
#ifdef USE_IP
|
||||
KnxIpDescriptionRequest::KnxIpDescriptionRequest(uint8_t* data, uint16_t length)
|
||||
: KnxIpFrame(data, length), _hpaiCtrl(data + LEN_KNXIP_HEADER)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
IpHostProtocolAddressInformation& KnxIpDescriptionRequest::hpaiCtrl()
|
||||
{
|
||||
return _hpaiCtrl;
|
||||
}
|
||||
#endif
|
||||
15
components/knx/src/knx/knx_ip_description_request.h
Normal file
15
components/knx/src/knx/knx_ip_description_request.h
Normal file
@ -0,0 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "knx_ip_frame.h"
|
||||
#include "knx_ip_cri.h"
|
||||
#include "ip_host_protocol_address_information.h"
|
||||
#ifdef USE_IP
|
||||
class KnxIpDescriptionRequest : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpDescriptionRequest(uint8_t* data, uint16_t length);
|
||||
IpHostProtocolAddressInformation& hpaiCtrl();
|
||||
private:
|
||||
IpHostProtocolAddressInformation _hpaiCtrl;
|
||||
};
|
||||
#endif
|
||||
72
components/knx/src/knx/knx_ip_description_response.cpp
Normal file
72
components/knx/src/knx/knx_ip_description_response.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
#include "knx_ip_description_response.h"
|
||||
#ifdef USE_IP
|
||||
|
||||
#define LEN_SERVICE_FAMILIES 2
|
||||
#if MASK_VERSION == 0x091A
|
||||
#ifdef KNX_TUNNELING
|
||||
#define LEN_SERVICE_DIB (2 + 4 * LEN_SERVICE_FAMILIES)
|
||||
#else
|
||||
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
|
||||
#endif
|
||||
#else
|
||||
#ifdef KNX_TUNNELING
|
||||
#define LEN_SERVICE_DIB (2 + 3 * LEN_SERVICE_FAMILIES)
|
||||
#else
|
||||
#define LEN_SERVICE_DIB (2 + 2 * LEN_SERVICE_FAMILIES)
|
||||
#endif
|
||||
#endif
|
||||
|
||||
KnxIpDescriptionResponse::KnxIpDescriptionResponse(IpParameterObject& parameters, DeviceObject& deviceObject)
|
||||
: KnxIpFrame(LEN_KNXIP_HEADER + LEN_DEVICE_INFORMATION_DIB + LEN_SERVICE_DIB),
|
||||
_deviceInfo(_data + LEN_KNXIP_HEADER),
|
||||
_supportedServices(_data + LEN_KNXIP_HEADER + LEN_DEVICE_INFORMATION_DIB)
|
||||
{
|
||||
serviceTypeIdentifier(DescriptionResponse);
|
||||
|
||||
_deviceInfo.length(LEN_DEVICE_INFORMATION_DIB);
|
||||
_deviceInfo.code(DEVICE_INFO);
|
||||
#if MASK_VERSION == 0x57B0
|
||||
_deviceInfo.medium(0x20); //MediumType is IP (for IP-Only Devices)
|
||||
#else
|
||||
_deviceInfo.medium(0x02); //MediumType is TP
|
||||
#endif
|
||||
_deviceInfo.status(deviceObject.progMode());
|
||||
_deviceInfo.individualAddress(parameters.propertyValue<uint16_t>(PID_KNX_INDIVIDUAL_ADDRESS));
|
||||
_deviceInfo.projectInstallationIdentifier(parameters.propertyValue<uint16_t>(PID_PROJECT_INSTALLATION_ID));
|
||||
_deviceInfo.serialNumber(deviceObject.propertyData(PID_SERIAL_NUMBER));
|
||||
_deviceInfo.routingMulticastAddress(parameters.propertyValue<uint32_t>(PID_ROUTING_MULTICAST_ADDRESS));
|
||||
//_deviceInfo.routingMulticastAddress(0);
|
||||
|
||||
uint8_t mac_address[LEN_MAC_ADDRESS] = {0};
|
||||
Property* prop = parameters.property(PID_MAC_ADDRESS);
|
||||
prop->read(mac_address);
|
||||
_deviceInfo.macAddress(mac_address);
|
||||
|
||||
uint8_t friendlyName[LEN_FRIENDLY_NAME] = {0};
|
||||
prop = parameters.property(PID_FRIENDLY_NAME);
|
||||
prop->read(1, LEN_FRIENDLY_NAME, friendlyName);
|
||||
_deviceInfo.friendlyName(friendlyName);
|
||||
|
||||
_supportedServices.length(LEN_SERVICE_DIB);
|
||||
_supportedServices.code(SUPP_SVC_FAMILIES);
|
||||
_supportedServices.serviceVersion(Core, 1);
|
||||
_supportedServices.serviceVersion(DeviceManagement, 1);
|
||||
#ifdef KNX_TUNNELING
|
||||
_supportedServices.serviceVersion(Tunnelling, 1);
|
||||
#endif
|
||||
#if MASK_VERSION == 0x091A
|
||||
_supportedServices.serviceVersion(Routing, 1);
|
||||
#endif
|
||||
}
|
||||
|
||||
KnxIpDeviceInformationDIB& KnxIpDescriptionResponse::deviceInfo()
|
||||
{
|
||||
return _deviceInfo;
|
||||
}
|
||||
|
||||
|
||||
KnxIpSupportedServiceDIB& KnxIpDescriptionResponse::supportedServices()
|
||||
{
|
||||
return _supportedServices;
|
||||
}
|
||||
#endif
|
||||
21
components/knx/src/knx/knx_ip_description_response.h
Normal file
21
components/knx/src/knx/knx_ip_description_response.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "knx_ip_frame.h"
|
||||
#include "ip_host_protocol_address_information.h"
|
||||
#include "knx_ip_device_information_dib.h"
|
||||
#include "knx_ip_supported_service_dib.h"
|
||||
#include "ip_parameter_object.h"
|
||||
#ifdef USE_IP
|
||||
|
||||
class KnxIpDescriptionResponse : public KnxIpFrame
|
||||
{
|
||||
public:
|
||||
KnxIpDescriptionResponse(IpParameterObject& parameters, DeviceObject& deviceObj);
|
||||
KnxIpDeviceInformationDIB& deviceInfo();
|
||||
KnxIpSupportedServiceDIB& supportedServices();
|
||||
private:
|
||||
KnxIpDeviceInformationDIB _deviceInfo;
|
||||
KnxIpSupportedServiceDIB _supportedServices;
|
||||
};
|
||||
|
||||
#endif
|
||||
102
components/knx/src/knx/knx_ip_device_information_dib.cpp
Normal file
102
components/knx/src/knx/knx_ip_device_information_dib.cpp
Normal 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
|
||||
32
components/knx/src/knx/knx_ip_device_information_dib.h
Normal file
32
components/knx/src/knx/knx_ip_device_information_dib.h
Normal 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
|
||||
28
components/knx/src/knx/knx_ip_dib.cpp
Normal file
28
components/knx/src/knx/knx_ip_dib.cpp
Normal 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
Loading…
Reference in New Issue
Block a user