From a7a48b4a7bc959575443c71292b41adbf4d3cf22 Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Fri, 16 Mar 2018 00:48:39 +0100 Subject: [PATCH 01/24] initial checkin --- .gitignore | 247 ++++++++++ address_table_object.cpp | 90 ++++ address_table_object.h | 20 + apdu.cpp | 51 +++ apdu.h | 22 + application_layer.cpp | 792 +++++++++++++++++++++++++++++++++ application_layer.h | 102 +++++ application_program_object.cpp | 84 ++++ application_program_object.h | 20 + association_table_object.cpp | 74 +++ association_table_object.h | 20 + bau.cpp | 238 ++++++++++ bau.h | 110 +++++ bau57B0.cpp | 272 +++++++++++ bau57B0.h | 74 +++ bits.cpp | 75 ++++ bits.h | 133 ++++++ cemi_frame.cpp | 195 ++++++++ cemi_frame.h | 60 +++ data_link_layer.cpp | 199 +++++++++ data_link_layer.h | 33 ++ datapoint_types.cpp | 63 +++ datapoint_types.h | 37 ++ device_object.cpp | 240 ++++++++++ device_object.h | 46 ++ esp_platform.cpp | 102 +++++ esp_platform.h | 36 ++ group_object.cpp | 191 ++++++++ group_object.h | 111 +++++ group_object_table_object.cpp | 125 ++++++ group_object_table_object.h | 27 ++ interface_object.h | 62 +++ ip_parameter_object.cpp | 298 +++++++++++++ ip_parameter_object.h | 42 ++ knx-esp.vcxitems | 75 ++++ knx-esp.vcxitems.filters | 171 +++++++ knx_esp.h | 19 + knx_types.h | 103 +++++ library.properties | 9 + memory.cpp | 47 ++ memory.h | 24 + network_layer.cpp | 126 ++++++ network_layer.h | 38 ++ npdu.cpp | 44 ++ npdu.h | 24 + platform.h | 26 ++ property_types.h | 166 +++++++ readme.txt | 19 + save_restore.h | 10 + table_object.cpp | 257 +++++++++++ table_object.h | 33 ++ tpdu.cpp | 127 ++++++ tpdu.h | 32 ++ transport_layer.cpp | 711 +++++++++++++++++++++++++++++ transport_layer.h | 98 ++++ 55 files changed, 6450 insertions(+) create mode 100644 .gitignore create mode 100644 address_table_object.cpp create mode 100644 address_table_object.h create mode 100644 apdu.cpp create mode 100644 apdu.h create mode 100644 application_layer.cpp create mode 100644 application_layer.h create mode 100644 application_program_object.cpp create mode 100644 application_program_object.h create mode 100644 association_table_object.cpp create mode 100644 association_table_object.h create mode 100644 bau.cpp create mode 100644 bau.h create mode 100644 bau57B0.cpp create mode 100644 bau57B0.h create mode 100644 bits.cpp create mode 100644 bits.h create mode 100644 cemi_frame.cpp create mode 100644 cemi_frame.h create mode 100644 data_link_layer.cpp create mode 100644 data_link_layer.h create mode 100644 datapoint_types.cpp create mode 100644 datapoint_types.h create mode 100644 device_object.cpp create mode 100644 device_object.h create mode 100644 esp_platform.cpp create mode 100644 esp_platform.h create mode 100644 group_object.cpp create mode 100644 group_object.h create mode 100644 group_object_table_object.cpp create mode 100644 group_object_table_object.h create mode 100644 interface_object.h create mode 100644 ip_parameter_object.cpp create mode 100644 ip_parameter_object.h create mode 100644 knx-esp.vcxitems create mode 100644 knx-esp.vcxitems.filters create mode 100644 knx_esp.h create mode 100644 knx_types.h create mode 100644 library.properties create mode 100644 memory.cpp create mode 100644 memory.h create mode 100644 network_layer.cpp create mode 100644 network_layer.h create mode 100644 npdu.cpp create mode 100644 npdu.h create mode 100644 platform.h create mode 100644 property_types.h create mode 100644 readme.txt create mode 100644 save_restore.h create mode 100644 table_object.cpp create mode 100644 table_object.h create mode 100644 tpdu.cpp create mode 100644 tpdu.h create mode 100644 transport_layer.cpp create mode 100644 transport_layer.h diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2ba06ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,247 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +[Xx]64/ +[Xx]86/ +[Bb]uild/ +bld/ +[Bb]in/ +[Oo]bj/ + +# Visual Studio 2015 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# DNX +project.lock.json +artifacts/ + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding add-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml + +# TODO: Un-comment the next line if you do not want to checkin +# your web deploy settings because they may include unencrypted +# passwords +#*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config +# NuGet v3's project.json files produces more ignoreable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Microsoft Azure ApplicationInsights config file +ApplicationInsights.config + +# Windows Store app package directory +AppPackages/ +BundleArtifacts/ + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!*.[Cc]ache/ + +# Others +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +orleans.codegen.cs + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# LightSwitch generated files +GeneratedArtifacts/ +ModelManifest.xml + +# Paket dependency manager +.paket/paket.exe + +# FAKE - F# Make +.fake/ +/CodeDB +/VisualGDBCache diff --git a/address_table_object.cpp b/address_table_object.cpp new file mode 100644 index 0000000..e1bf3d0 --- /dev/null +++ b/address_table_object.cpp @@ -0,0 +1,90 @@ +#include + +#include "address_table_object.h" +#include "bits.h" + +using namespace std; + + +AddressTableObject::AddressTableObject(uint8_t* memoryReference): TableObject(memoryReference) +{ + +} + +void AddressTableObject::readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data) +{ + switch (id) + { + case PID_OBJECT_TYPE: + pushWord(OT_ADDR_TABLE, data); + break; + default: + TableObject::readProperty(id, start, count, data); + } +} + +uint16_t AddressTableObject::entryCount() +{ + if (loadState() != LS_LOADED) + return 0; + + return _groupAddresses[0]; +} + +uint16_t AddressTableObject::getGa(uint16_t tsap) +{ + if (loadState() != LS_LOADED || tsap > entryCount() ) + return 0; + + return _groupAddresses[tsap]; +} + +uint16_t AddressTableObject::getTsap(uint16_t addr) +{ + uint16_t size = entryCount(); + for (uint16_t i = 1; i <= size; i++) + if (_groupAddresses[i] == addr) + return i; + return 1; +} + +#pragma region SaveRestore + +uint8_t* AddressTableObject::save(uint8_t* buffer) +{ + return TableObject::save(buffer); +} + +uint8_t* AddressTableObject::restore(uint8_t* buffer) +{ + buffer = TableObject::restore(buffer); + + _groupAddresses = (uint16_t*)_data; + + return buffer; +} + +#pragma endregion + +bool AddressTableObject::contains(uint16_t addr) +{ + uint16_t size = entryCount(); + for (uint16_t i = 1; i <= size; i++) + if (_groupAddresses[i] == addr) + return true; + + return false; +} + +void AddressTableObject::beforeStateChange(LoadState& newState) +{ + if (newState != LS_LOADED) + return; + + _groupAddresses = (uint16_t*)_data; + + uint16_t count = reverseByteOrder(_groupAddresses[0]); + // big endian -> little endian + for (size_t i = 0; i <= count; i++) + _groupAddresses[i] = reverseByteOrder(_groupAddresses[i]); +} diff --git a/address_table_object.h b/address_table_object.h new file mode 100644 index 0000000..c7c6022 --- /dev/null +++ b/address_table_object.h @@ -0,0 +1,20 @@ +#pragma once + +#include "table_object.h" + +class AddressTableObject: public TableObject +{ +public: + AddressTableObject(uint8_t* memoryReference); + void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data); + uint16_t entryCount(); + uint16_t getGa(uint16_t tsap); + uint16_t getTsap(uint16_t ga); + uint8_t* save(uint8_t* buffer); + uint8_t* restore(uint8_t* buffer); + bool contains(uint16_t addr); +protected: + virtual void beforeStateChange(LoadState& newState); +private: + uint16_t* _groupAddresses; +}; \ No newline at end of file diff --git a/apdu.cpp b/apdu.cpp new file mode 100644 index 0000000..26b86bd --- /dev/null +++ b/apdu.cpp @@ -0,0 +1,51 @@ +#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; + popWord(apci, _data); + apci &= 0x3ff; + if ((apci >> 6) < 11) //short apci + apci &= 0x3c0; + return (ApduType)apci; +} + +void APDU::type(ApduType atype) +{ + 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.print("APDU: "); + //print.print(type(), HEX, 4); + //print.print(" "); + //print.print(_data[0] & 0x3, HEX, 2); + //for (uint8_t i = 1; i < length() + 1; ++i) + //{ + // if (i) print.print(" "); + // print.print(_data[i], HEX, 2); + //} + //print.println(); +} diff --git a/apdu.h b/apdu.h new file mode 100644 index 0000000..e729783 --- /dev/null +++ b/apdu.h @@ -0,0 +1,22 @@ +#pragma once + +#include +#include "knx_types.h" + +class CemiFrame; + +class APDU +{ + friend class CemiFrame; +public: + APDU(uint8_t* data, CemiFrame& frame); + ApduType type(); + void type(ApduType atype); + uint8_t* data(); + CemiFrame& frame(); + uint8_t length() const; + void printPDU(); +private: + uint8_t* _data; + CemiFrame& _frame; +}; \ No newline at end of file diff --git a/application_layer.cpp b/application_layer.cpp new file mode 100644 index 0000000..62233f0 --- /dev/null +++ b/application_layer.cpp @@ -0,0 +1,792 @@ +#include "application_layer.h" +#include "transport_layer.h" +#include "cemi_frame.h" +#include "association_table_object.h" +#include "apdu.h" +#include "bau.h" +#include "string.h" +#include "bits.h" +#include + +ApplicationLayer::ApplicationLayer(AssociationTableObject& assocTable, BusAccessUnit& bau): + _assocTable(assocTable), _bau(bau) +{ +} + +void ApplicationLayer::transportLayer(TransportLayer& layer) +{ + _transportLayer = &layer; +} + +#pragma region TL Callbacks + +void ApplicationLayer::dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) +{ + uint16_t entries = _assocTable.entryCount(); + + uint8_t len = apdu.length(); + uint8_t dataArray[len]; + uint8_t* data = dataArray; + memcpy(data, apdu.data(), len); + if (len == 1) + { + //less than six bit are encoded in first byte + *data &= 0x3f; + } + else + { + data += 1; + len -= 1; + } + + for (uint16_t i; i < entries; i++) + { + uint16_t entry = _assocTable[i]; + if (highByte(entry) == tsap) + { + uint16_t asap = lowByte(entry); + switch (apdu.type()) + { + case GroupValueRead: + _bau.groupValueReadIndication(asap, priority, hopType); + break; + case GroupValueResponse: + _bau.groupValueReadAppLayerConfirm(asap, priority, hopType, data, len); + break; + case GroupValueWrite: + _bau.groupValueWriteIndication(asap, priority, hopType, data, len); + } + } + } +} + +void ApplicationLayer::dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status) +{ + switch (apdu.type()) + { + case GroupValueRead: + _bau.groupValueReadLocalConfirm(ack, _savedAsapReadRequest, priority, hopType, status); + break; + case GroupValueResponse: + _bau.groupValueReadResponseConfirm(ack, _savedAsapResponse, priority, hopType, apdu.data(), apdu.length() - 1, status); + break; + case GroupValueWrite: + _bau.groupValueWriteLocalConfirm(ack, _savedAsapWriteRequest, priority, hopType, apdu.data(), apdu.length() - 1, status); + break; + } +} + +void ApplicationLayer::dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) +{ + uint8_t* data = apdu.data(); + switch (apdu.type()) + { + case IndividualAddressWrite: + { + uint16_t newAddress; + popWord(newAddress, data + 1); + _bau.individualAddressWriteIndication(hopType, newAddress); + break; + } + case IndividualAddressRead: + _bau.individualAddressReadIndication(hopType); + break; + case IndividualAddressResponse: + _bau.individualAddressReadAppLayerConfirm(hopType, apdu.frame().sourceAddress()); + break; + case IndividualAddressSerialNumberRead: + _bau.individualAddressSerialNumberReadIndication(hopType, data + 1); + break; + case IndividualAddressSerialNumberResponse: + { + uint16_t domainAddress; + popWord(domainAddress, data + 7); + _bau.individualAddressSerialNumberReadAppLayerConfirm(hopType, data + 1, apdu.frame().sourceAddress(), + domainAddress); + break; + } + case IndividualAddressSerialNumberWrite: + { + uint16_t newAddress; + popWord(newAddress, data + 7); + _bau.individualAddressSerialNumberWriteIndication(hopType, data + 1, newAddress); + break; + } + } +} + +void ApplicationLayer::dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, bool status) +{ + uint8_t* data = apdu.data(); + switch (apdu.type()) + { + case IndividualAddressWrite: + { + uint16_t newAddress; + popWord(newAddress, data + 1); + _bau.individualAddressWriteLocalConfirm(ack, hopType, newAddress, status); + break; + } + case IndividualAddressRead: + _bau.individualAddressReadLocalConfirm(ack, hopType, status); + break; + case IndividualAddressResponse: + _bau.individualAddressReadResponseConfirm(ack, hopType, status); + break; + case IndividualAddressSerialNumberRead: + _bau.individualAddressSerialNumberReadLocalConfirm(ack, hopType, data + 1, status); + break; + case IndividualAddressSerialNumberResponse: + { + uint16_t domainAddress; + popWord(domainAddress, data + 7); + _bau.individualAddressSerialNumberReadResponseConfirm(ack, hopType, data + 1, domainAddress, status); + break; + } + case IndividualAddressSerialNumberWrite: + { + uint16_t newAddress; + popWord(newAddress, data + 7); + _bau.individualAddressSerialNumberWriteLocalConfirm(ack, hopType, data + 1, newAddress, status); + break; + } + } +} + +void ApplicationLayer::dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) +{ + +} + +void ApplicationLayer::dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status) +{ + +} + +void ApplicationLayer::dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) +{ + individualIndication(hopType, priority, tsap, apdu); +} + +void ApplicationLayer::dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status) +{ + individualConfirm(ack, hopType, priority, tsap, apdu, status); +} + +void ApplicationLayer::connectIndication(uint16_t tsap) +{ + _connectedTsap = tsap; +} + +void ApplicationLayer::connectConfirm(uint16_t destination, uint16_t tsap, bool status) +{ + +} + +void ApplicationLayer::disconnectIndication(uint16_t tsap) +{ + _connectedTsap = -1; +} + +void ApplicationLayer::disconnectConfirm(Priority priority, uint16_t tsap, bool status) +{ + +} + +void ApplicationLayer::dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu) +{ + individualIndication(NetworkLayerParameter, priority, tsap, apdu); +} + +void ApplicationLayer::dataConnectedConfirm(uint16_t tsap) +{ + +} +#pragma endregion +void ApplicationLayer::groupValueReadRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType) +{ + _savedAsapReadRequest = asap; + CemiFrame frame(1); + APDU& apdu = frame.apdu(); + apdu.type(GroupValueRead); + + int32_t value = _assocTable.translateAsap(asap); + if (value < 0) + return; // there is no tsap in association table for this asap + + uint16_t tsap = (uint16_t)value; + + // first to bus then to itself + _transportLayer->dataGroupRequest(ack, hopType, priority, tsap, apdu); + dataGroupIndication(hopType, priority, tsap, apdu); +} + +void ApplicationLayer::groupValueReadResponse(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength) +{ + _savedAsapResponse = asap; + groupValueSend(GroupValueResponse, ack, asap, priority, hopType, data, dataLength); +} + +void ApplicationLayer::groupValueWriteRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength) +{ + _savedAsapWriteRequest = asap; + groupValueSend(GroupValueWrite, ack, asap, priority, hopType, data, dataLength); +} + +void ApplicationLayer::individualAddressWriteRequest(AckType ack, HopCountType hopType, uint16_t newaddress) +{ + CemiFrame frame(3); + APDU& apdu = frame.apdu(); + apdu.type(IndividualAddressWrite); + uint8_t* apduData = apdu.data(); + pushWord(newaddress, apduData + 1); + _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); +} + +void ApplicationLayer::individualAddressReadRequest(AckType ack, HopCountType hopType) +{ + CemiFrame frame(1); + APDU& apdu = frame.apdu(); + apdu.type(IndividualAddressRead); + _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); +} + +void ApplicationLayer::individualAddressReadResponse(AckType ack, HopCountType hopType) +{ + CemiFrame frame(1); + APDU& apdu = frame.apdu(); + apdu.type(IndividualAddressResponse); + _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); +} + +void ApplicationLayer::individualAddressSerialNumberReadRequest(AckType ack, HopCountType hopType, uint8_t * serialNumber) +{ + CemiFrame frame(7); + APDU& apdu = frame.apdu(); + apdu.type(IndividualAddressSerialNumberRead); + uint8_t* data = apdu.data() + 1; + memcpy(data, serialNumber, 6); + _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); +} + +void ApplicationLayer::individualAddressSerialNumberReadResponse(AckType ack, HopCountType hopType, + uint8_t * serialNumber, uint16_t domainAddress) +{ + CemiFrame frame(7); + APDU& apdu = frame.apdu(); + apdu.type(IndividualAddressSerialNumberResponse); + uint8_t* data = apdu.data() + 1; + memcpy(data, serialNumber, 6); + data += 6; + pushWord(domainAddress, data); + _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); +} + +void ApplicationLayer::individualAddressSerialNumberWriteRequest(AckType ack, HopCountType hopType, uint8_t * serialNumber, + uint16_t newaddress) +{ + CemiFrame frame(13); + APDU& apdu = frame.apdu(); + apdu.type(IndividualAddressSerialNumberWrite); + uint8_t* data = apdu.data() + 1; + memcpy(data, serialNumber, 6); + data += 6; + pushWord(newaddress, data); + _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); +} + +void ApplicationLayer::deviceDescriptorReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t descriptorType) +{ + CemiFrame frame(1); + APDU& apdu = frame.apdu(); + apdu.type(DeviceDescriptorRead); + uint8_t* data = apdu.data(); + *data |= (descriptorType & 0x3f); + + individualSend(ack, hopType, priority, asap, apdu); +} + +void ApplicationLayer::deviceDescriptorReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t descriptorType, uint8_t* deviceDescriptor) +{ + uint8_t length = 0; + switch (descriptorType) + { + case 0: + length = 3; + break; + case 2: + length = 14; + break; + default: + length = 1; + descriptorType = 0x3f; + break; + } + CemiFrame frame(length); + APDU& apdu = frame.apdu(); + apdu.type(DeviceDescriptorResponse); + uint8_t* data = apdu.data(); + *data |= (descriptorType & 0x3f); + + if (length > 1) + memcpy(data + 1, deviceDescriptor, length - 1); + + individualSend(ack, hopType, priority, asap, apdu); +} + +void ApplicationLayer::restartRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap) +{ + CemiFrame frame(1); + APDU& apdu = frame.apdu(); + apdu.type(Restart); + + individualSend(ack, hopType, priority, asap, apdu); +} + +void ApplicationLayer::propertyValueReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex) +{ + CemiFrame frame(5); + APDU& apdu = frame.apdu(); + apdu.type(PropertyValueRead); + uint8_t* data = apdu.data(); + data += 1; + data = pushByte(objectIndex, data); + data = pushByte(propertyId, data); + pushWord(startIndex & 0xfff, data); + *data &= ((numberOfElements & 0xf) << 4); + + individualSend(ack, hopType, priority, asap, apdu); +} + +void ApplicationLayer::propertyValueReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) +{ + propertyDataSend(PropertyValueResponse, ack, priority, hopType, asap, objectIndex, propertyId, numberOfElements, + startIndex, data, length); +} + +void ApplicationLayer::propertyValueWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t * data, uint8_t length) +{ + propertyDataSend(PropertyValueWrite, ack, priority, hopType, asap, objectIndex, propertyId, numberOfElements, + startIndex, data, length); +} + +void ApplicationLayer::propertyDescriptionReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex) +{ + CemiFrame frame(4); + APDU& apdu = frame.apdu(); + apdu.type(PropertyDescriptionRead); + uint8_t* data = apdu.data(); + data[1] = objectIndex; + data[2] = propertyId; + data[3] = propertyIndex; + individualSend(ack, hopType, priority, asap, apdu); +} + +void ApplicationLayer::propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, + uint16_t maxNumberOfElements, uint8_t access) +{ + CemiFrame frame(8); + APDU& apdu = frame.apdu(); + apdu.type(PropertyDescriptionRead); + uint8_t* data = apdu.data(); + data[1] = objectIndex; + data[2] = propertyId; + data[3] = propertyIndex; + if (writeEnable) + data[4] |= 0x80; + data[4] |= (type & 0x3f); + pushWord(maxNumberOfElements & 0xfff, data + 5); + data[7] = access; + individualSend(ack, hopType, priority, asap, apdu); +} + +void ApplicationLayer::memoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint16_t memoryAddress) +{ + CemiFrame frame(3); + APDU& apdu = frame.apdu(); + apdu.type(MemoryRead); + uint8_t* data = apdu.data(); + *data |= (number & 0x3f); + pushWord(memoryAddress, data + 1); + individualSend(ack, hopType, priority, asap, apdu); +} + +void ApplicationLayer::memoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint16_t memoryAddress, uint8_t * memoryData) +{ + memorySend(MemoryResponse, ack, priority, hopType, asap, number, memoryAddress, memoryData); +} + +void ApplicationLayer::memoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t number, uint16_t memoryAddress, uint8_t * data) +{ + memorySend(MemoryWrite, ack, priority, hopType, asap, number, memoryAddress, data); +} + +void ApplicationLayer::userMemoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t number, uint32_t memoryAddress) +{ + CemiFrame frame(4); + APDU& apdu = frame.apdu(); + apdu.type(UserMemoryRead); + uint8_t* data = apdu.data(); + data[1] |= (number & 0xf); + data[1] |= ((memoryAddress >> 12) & 0xf0); + pushWord(memoryAddress & 0xff, data + 2); + individualSend(ack, hopType, priority, asap, apdu); +} + +void ApplicationLayer::userMemoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t number, uint32_t memoryAddress, uint8_t * memoryData) +{ + userMemorySend(UserMemoryResponse, ack, priority, hopType, asap, number, memoryAddress, memoryData); +} + +void ApplicationLayer::userMemoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t number, uint32_t memoryAddress, uint8_t * memoryData) +{ + userMemorySend(UserMemoryWrite, ack, priority, hopType, asap, number, memoryAddress, memoryData); +} + +void ApplicationLayer::userManufacturerInfoReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap) +{ + CemiFrame frame(1); + APDU& apdu = frame.apdu(); + apdu.type(UserManufacturerInfoRead); + individualSend(ack, hopType, priority, asap, apdu); +} + +void ApplicationLayer::userManufacturerInfoReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t* info) +{ + CemiFrame frame(4); + APDU& apdu = frame.apdu(); + apdu.type(UserMemoryRead); + uint8_t* data = apdu.data(); + memcpy(data + 1, info, 3); + individualSend(ack, hopType, priority, asap, apdu); +} + +void ApplicationLayer::authorizeRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint32_t key) +{ + CemiFrame frame(6); + APDU& apdu = frame.apdu(); + apdu.type(AuthorizeRequest); + uint8_t* data = apdu.data(); + pushInt(key, data + 2); + individualSend(ack, hopType, priority, asap, apdu); +} + +void ApplicationLayer::authorizeResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level) +{ + CemiFrame frame(2); + APDU& apdu = frame.apdu(); + apdu.type(AuthorizeResponse); + uint8_t* data = apdu.data(); + data[1] = level; + individualSend(ack, hopType, priority, asap, apdu); +} + +void ApplicationLayer::keyWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, uint32_t key) +{ + CemiFrame frame(6); + APDU& apdu = frame.apdu(); + apdu.type(KeyWrite); + uint8_t* data = apdu.data(); + data[1] = level; + pushInt(key, data + 2); + individualSend(ack, hopType, priority, asap, apdu); +} + +void ApplicationLayer::keyWriteResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level) +{ + CemiFrame frame(6); + APDU& apdu = frame.apdu(); + apdu.type(KeyResponse); + uint8_t* data = apdu.data(); + data[1] = level; + individualSend(ack, hopType, priority, asap, apdu); +} + +void ApplicationLayer::propertyDataSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) +{ + CemiFrame frame(5 + length); + APDU& apdu = frame.apdu(); + apdu.type(type); + uint8_t* apduData = apdu.data(); + apduData += 1; + apduData = pushByte(objectIndex, apduData); + apduData = pushByte(propertyId, apduData); + pushWord(startIndex & 0xfff, apduData); + *apduData |= ((numberOfElements & 0xf) << 4); + apduData += 2; + if (length > 0) + memcpy(apduData, data, length); + + if (asap == _connectedTsap) + _transportLayer->dataConnectedRequest(asap, priority, apdu); + else + _transportLayer->dataIndividualRequest(ack, hopType, priority, asap, apdu); +} + +void ApplicationLayer::groupValueSend(ApduType type, AckType ack, uint16_t asap, Priority priority, HopCountType hopType, + uint8_t* data, uint8_t& dataLength) +{ + CemiFrame frame(dataLength + 1); + APDU& apdu = frame.apdu(); + apdu.type(type); + uint8_t* apdudata = apdu.data(); + if (dataLength == 0) + { + // data size is six bit or less. So store int first byte + *apdudata &= ~0x3f; + *apdudata |= (*data & 0x3f); + } + else + { + memcpy(apdudata + 1, data, dataLength); + } + // no need to check if there is a tsap. This is a response, so the read got trough + uint16_t tsap = (uint16_t)_assocTable.translateAsap(asap); + _transportLayer->dataGroupRequest(ack, hopType, priority, tsap, apdu); + dataGroupIndication(hopType, priority, tsap, apdu); +} + +void ApplicationLayer::memorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint16_t memoryAddress, uint8_t * memoryData) +{ + CemiFrame frame(3 + number); + APDU& apdu = frame.apdu(); + apdu.type(type); + uint8_t* data = apdu.data(); + *data |= (number & 0x3f); + pushWord(memoryAddress, data + 1); + if (number > 0) + memcpy(data + 3, memoryData, number); + + individualSend(ack, hopType, priority, asap, apdu); +} + +void ApplicationLayer::userMemorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint32_t memoryAddress, uint8_t * memoryData) +{ + CemiFrame frame(4 + number); + APDU& apdu = frame.apdu(); + apdu.type(type); + uint8_t* data = apdu.data(); + data[1] |= (number & 0xf); + data[1] |= ((memoryAddress >> 12) & 0xf0); + pushWord(memoryAddress & 0xffff, data + 2); + if (number > 0) + memcpy(data + 4, memoryData, number); + individualSend(ack, hopType, priority, asap, apdu); +} + +void ApplicationLayer::individualIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU & apdu) +{ + uint8_t* data = apdu.data(); + switch (apdu.type()) + { + case DeviceDescriptorRead: + _bau.deviceDescriptorReadIndication(priority, hopType, tsap, *data & 0x3f); + break; + case DeviceDescriptorResponse: + _bau.deviceDescriptorReadAppLayerConfirm(priority, hopType, tsap, *data & 0x3f, data + 1); + break; + case Restart: + if ((*data & 0x3f) == 0) + _bau.restartRequestIndication(priority, hopType, tsap); + break; + case PropertyValueRead: + { + uint16_t startIndex; + popWord(startIndex, data + 3); + startIndex &= 0xfff; + _bau.propertyValueReadIndication(priority, hopType, tsap, data[1], data[2], data[3] >> 4, startIndex); + break; + } + case PropertyValueResponse: + { + uint16_t startIndex; + popWord(startIndex, data + 3); + startIndex &= 0xfff; + _bau.propertyValueReadAppLayerConfirm(priority, hopType, tsap, data[1], data[2], data[3] >> 4, + startIndex, data + 5, apdu.length() - 5); + break; + } + case PropertyValueWrite: + { + uint16_t startIndex; + popWord(startIndex, data + 3); + startIndex &= 0xfff; + _bau.propertyValueWriteIndication(priority, hopType, tsap, data[1], data[2], data[3] >> 4, + startIndex, data + 5, apdu.length() - 5); + break; + } + case PropertyDescriptionRead: + _bau.propertyDescriptionReadIndication(priority, hopType, tsap, data[1], data[2], data[3]); + break; + case PropertyDescriptionResponse: + _bau.propertyDescriptionReadAppLayerConfirm(priority, hopType, tsap, data[1], data[2], data[3], + (data[4] & 0x80) > 0, data[4] & 0x3f, getWord(data + 5) & 0xfff, data[7]); + break; + case MemoryRead: + _bau.memoryReadIndication(priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1)); + break; + case MemoryResponse: + _bau.memoryReadAppLayerConfirm(priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), data + 3); + break; + case MemoryWrite: + _bau.memoryWriteIndication(priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), data + 3); + break; + case UserMemoryRead: + { + uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; + _bau.userMemoryReadIndication(priority, hopType, tsap, data[1] & 0xf, address); + break; + } + case UserMemoryResponse: + { + uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; + _bau.userMemoryReadAppLayerConfirm(priority, hopType, tsap, data[1] & 0xf, address, data + 4); + break; + } + case UserMemoryWrite: + { + uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; + _bau.userMemoryWriteIndication(priority, hopType, tsap, data[1] & 0xf, address, data + 4); + break; + } + case UserManufacturerInfoRead: + _bau.userManufacturerInfoIndication(priority, hopType, tsap); + break; + case UserManufacturerInfoResponse: + _bau.userManufacturerInfoAppLayerConfirm(priority, hopType, tsap, data + 1); + break; + case AuthorizeRequest: + _bau.authorizeIndication(priority, hopType, tsap, getInt(data + 2)); + break; + case AuthorizeResponse: + _bau.authorizeAppLayerConfirm(priority, hopType, tsap, data[1]); + break; + case KeyWrite: + _bau.keyWriteIndication(priority, hopType, tsap, data[1], getInt(data + 2)); + break; + case KeyResponse: + _bau.keyWriteAppLayerConfirm(priority, hopType, tsap, data[1]); + break; + } +} + +void ApplicationLayer::individualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU & apdu, bool status) +{ + uint8_t* data = apdu.data(); + switch (apdu.type()) + { + case DeviceDescriptorRead: + _bau.deviceDescriptorReadLocalConfirm(ack, priority, hopType, tsap, *data & 0x3f, status); + break; + case DeviceDescriptorResponse: + _bau.deviceDescriptorReadResponseConfirm(ack, priority, hopType, tsap, *data & 0x3f, data + 1, status); + break; + case Restart: + _bau.restartRequestLocalConfirm(ack, priority, hopType, tsap, status); + break; + case PropertyValueRead: + { + uint16_t startIndex; + popWord(startIndex, data + 3); + startIndex &= 0xfff; + _bau.propertyValueReadLocalConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3] >> 4, + startIndex, status); + break; + } + case PropertyValueResponse: + { + uint16_t startIndex; + popWord(startIndex, data + 3); + startIndex &= 0xfff; + _bau.propertyValueReadResponseConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3] >> 4, + startIndex, data + 5, apdu.length() - 5, status); + break; + } + case PropertyValueWrite: + { + uint16_t startIndex; + popWord(startIndex, data + 3); + startIndex &= 0xfff; + _bau.propertyValueWriteLocalConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3] >> 4, + startIndex, data + 5, apdu.length() - 5, status); + break; + } + case PropertyDescriptionRead: + _bau.propertyDescriptionReadLocalConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3], status); + break; + case PropertyDescriptionResponse: + _bau.propertyDescriptionReadResponseConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3], + (data[4] & 0x80) > 0, data[4] & 0x3f, getWord(data + 5) & 0xfff, data[7], status); + break; + case MemoryRead: + _bau.memoryReadLocalConfirm(ack, priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), status); + break; + case MemoryResponse: + _bau.memoryReadResponseConfirm(ack, priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), data + 3, status); + break; + case MemoryWrite: + _bau.memoryWriteLocalConfirm(ack, priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), data + 3, status); + break; + case UserMemoryRead: + { + uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; + _bau.memoryReadLocalConfirm(ack, priority, hopType, tsap, data[1] & 0xf, address, status); + break; + } + case UserMemoryResponse: + { + uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; + _bau.memoryReadResponseConfirm(ack, priority, hopType, tsap, data[1] & 0xf, address, data + 4, status); + break; + } + case UserMemoryWrite: + { + uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; + _bau.memoryWriteLocalConfirm(ack, priority, hopType, tsap, data[1] & 0xf, address, data + 4, status); + break; + } + case UserManufacturerInfoRead: + _bau.userManufacturerInfoLocalConfirm(ack, priority, hopType, tsap, status); + break; + case UserManufacturerInfoResponse: + _bau.userManufacturerInfoResponseConfirm(ack, priority, hopType, tsap, data + 1, status); + break; + case AuthorizeRequest: + _bau.authorizeLocalConfirm(ack, priority, hopType, tsap, getInt(data + 2), status); + break; + case AuthorizeResponse: + _bau.authorizeResponseConfirm(ack, priority, hopType, tsap, data[1], status); + break; + case KeyWrite: + _bau.keyWriteLocalConfirm(ack, priority, hopType, tsap, data[1], getInt(data + 2), status); + break; + case KeyResponse: + _bau.keyWriteResponseConfirm(ack, priority, hopType, tsap, data[1], status); + break; + } +} + +void ApplicationLayer::individualSend(AckType ack, HopCountType hopType, Priority priority, uint16_t asap, APDU& apdu) +{ + if (asap == _connectedTsap) + _transportLayer->dataConnectedRequest(asap, priority, apdu); + else + _transportLayer->dataIndividualRequest(ack, hopType, priority, asap, apdu); +} diff --git a/application_layer.h b/application_layer.h new file mode 100644 index 0000000..bd49216 --- /dev/null +++ b/application_layer.h @@ -0,0 +1,102 @@ +#pragma once + +#include +#include "knx_types.h" +#include "apdu.h" + +class AssociationTableObject; +class BusAccessUnit; +class TransportLayer; + +class ApplicationLayer +{ +public: + ApplicationLayer(AssociationTableObject& assocTable, BusAccessUnit& bau); + void transportLayer(TransportLayer& layer); + + // from transport layer +#pragma region Transport-Layer-Callbacks + void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu); + void dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, + APDU& apdu, bool status); + void dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu); + void dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, bool status); + void dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu); + void dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status); + void dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu); + void dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status); + 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); + void dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu); + void dataConnectedConfirm(uint16_t tsap); +#pragma endregion + + void groupValueReadRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType); + void groupValueReadResponse(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, uint8_t dataLength); + void groupValueWriteRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, uint8_t dataLength); + void individualAddressWriteRequest(AckType ack, HopCountType hopType, uint16_t newaddress); + void individualAddressReadRequest(AckType ack, HopCountType hopType); + void individualAddressReadResponse(AckType ack, HopCountType hopType); + void individualAddressSerialNumberReadRequest(AckType ack, HopCountType hopType, uint8_t* serialNumber); + void individualAddressSerialNumberReadResponse(AckType ack, HopCountType hopType, uint8_t* serialNumber, + uint16_t domainAddress); + void individualAddressSerialNumberWriteRequest(AckType ack, HopCountType hopType, uint8_t* serialNumber, + uint16_t newaddress); + void deviceDescriptorReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t descriptorType); + void deviceDescriptorReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t descriptorType, uint8_t* deviceDescriptor); + void restartRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap); + void propertyValueReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex); + void propertyValueReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length); + void propertyValueWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length); + void propertyDescriptionReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex); + void propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, + uint16_t maxNumberOfElements, uint8_t access); + void memoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint16_t memoryAddress); + void memoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint16_t memoryAddress, uint8_t* data); + void memoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint16_t memoryAddress, uint8_t* data); + void userMemoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint32_t memoryAddress); + void userMemoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint32_t memoryAddress, uint8_t* memoryData); + void userMemoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint32_t memoryAddress, uint8_t* memoryData); + void userManufacturerInfoReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap); + void userManufacturerInfoReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t* info); + void authorizeRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint32_t key); + void authorizeResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level); + void keyWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, uint32_t key); + void keyWriteResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level); +private: + void propertyDataSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t objectIndex, 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, uint8_t number, + uint16_t memoryAddress, uint8_t * memoryData); + void userMemorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t number, uint32_t memoryAddress, uint8_t* memoryData); + void groupValueSend(ApduType type, AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, uint8_t& dataLength); + void individualIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu); + void individualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status); + void individualSend(AckType ack, HopCountType hopType, Priority priority, uint16_t asap, APDU& apdu); + + uint16_t _savedAsapReadRequest; + uint16_t _savedAsapWriteRequest; + uint16_t _savedAsapResponse; + AssociationTableObject& _assocTable; + BusAccessUnit& _bau; + TransportLayer* _transportLayer; + int32_t _connectedTsap; +}; diff --git a/application_program_object.cpp b/application_program_object.cpp new file mode 100644 index 0000000..4db44ce --- /dev/null +++ b/application_program_object.cpp @@ -0,0 +1,84 @@ +#include "application_program_object.h" +#include "bits.h" + +ApplicationProgramObject::ApplicationProgramObject(uint8_t* memoryReference): TableObject(memoryReference) +{ + +} + +void ApplicationProgramObject::readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data) +{ + switch (id) + { + case PID_OBJECT_TYPE: + pushWord(OT_APPLICATION_PROG, data); + break; + case PID_PROG_VERSION: + pushByteArray(_programVersion, 5, data); + break; + case PID_PEI_TYPE: + pushByte(0x0, data); + break; + default: + TableObject::readProperty(id, start, count, data); + } +} + +void ApplicationProgramObject::writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count) +{ + switch (id) + { + case PID_PROG_VERSION: + for (uint32_t i = 0; i < 5; i++) + { + _programVersion[i] = data[i]; + } + break; + default: + TableObject::writeProperty(id, start, data, count); + } +} + +uint8_t ApplicationProgramObject::propertySize(PropertyID id) +{ + switch (id) + { + case PID_PROG_VERSION: + return 5; + } + return TableObject::propertySize(id); +} + +uint8_t * ApplicationProgramObject::data(uint32_t addr) +{ + return _data + addr; +} + +uint8_t ApplicationProgramObject::getByte(uint32_t addr) +{ + return *(_data + addr); +} + +uint16_t ApplicationProgramObject::getWord(uint32_t addr) +{ + return ::getWord(_data + addr); +} + +uint32_t ApplicationProgramObject::getInt(uint32_t addr) +{ + return ::getInt(_data + addr); +} + +uint8_t* ApplicationProgramObject::save(uint8_t* buffer) +{ + buffer = pushByteArray(_programVersion, 5, buffer); + + return TableObject::save(buffer); +} + +uint8_t* ApplicationProgramObject::restore(uint8_t* buffer) +{ + buffer = popByteArray(_programVersion, 5, buffer); + + return TableObject::restore(buffer); +} diff --git a/application_program_object.h b/application_program_object.h new file mode 100644 index 0000000..da4caf0 --- /dev/null +++ b/application_program_object.h @@ -0,0 +1,20 @@ +#pragma once + +#include "table_object.h" + +class ApplicationProgramObject: public TableObject +{ +public: + ApplicationProgramObject(uint8_t* memoryReference); + void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data); + void writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count); + uint8_t propertySize(PropertyID id); + uint8_t* data(uint32_t addr); + uint8_t getByte(uint32_t addr); + uint16_t getWord(uint32_t addr); + uint32_t getInt(uint32_t addr); + uint8_t* save(uint8_t* buffer); + uint8_t* restore(uint8_t* buffer); +private: + uint8_t _programVersion[5]; +}; \ No newline at end of file diff --git a/association_table_object.cpp b/association_table_object.cpp new file mode 100644 index 0000000..c7e21df --- /dev/null +++ b/association_table_object.cpp @@ -0,0 +1,74 @@ +#include + +#include "association_table_object.h" +#include "bits.h" + +using namespace std; + + +AssociationTableObject::AssociationTableObject(uint8_t* memoryReference): TableObject(memoryReference) +{ + +} + +void AssociationTableObject::readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data) +{ + switch (id) + { + case PID_OBJECT_TYPE: + pushWord(OT_ASSOC_TABLE, data); + break; + default: + TableObject::readProperty(id, start, count, data); + } +} + +uint16_t AssociationTableObject::entryCount() +{ + return _tableData[0]; +} + +uint16_t AssociationTableObject::operator[](uint16_t idx) +{ + if (idx < 0 || idx >= entryCount()) + return 0; + + return _tableData[idx + 1]; +} + +uint8_t* AssociationTableObject::save(uint8_t* buffer) +{ + return TableObject::save(buffer); +} + +uint8_t* AssociationTableObject::restore(uint8_t* buffer) +{ + buffer = TableObject::restore(buffer); + _tableData = (uint16_t*)_data; + return buffer; +} + +int32_t AssociationTableObject::translateAsap(uint16_t asap) +{ + uint16_t entries = entryCount(); + for (uint16_t i = 0; i < entries; i++) + { + uint16_t entry = operator[](i); + if (lowByte(entry) == asap) + return highByte(entry); + } + return -1; +} + +void AssociationTableObject::beforeStateChange(LoadState& newState) +{ + if (newState != LS_LOADED) + return; + + _tableData = (uint16_t*)_data; + + uint16_t count = reverseByteOrder(_tableData[0]); + // big endian -> little endian + for (size_t i = 0; i <= count; i++) + _tableData[i] = reverseByteOrder(_tableData[i]); +} \ No newline at end of file diff --git a/association_table_object.h b/association_table_object.h new file mode 100644 index 0000000..ee4bd7d --- /dev/null +++ b/association_table_object.h @@ -0,0 +1,20 @@ +#pragma once + +#include "table_object.h" + +class AssociationTableObject: public TableObject +{ +public: + AssociationTableObject(uint8_t* memoryReference); + void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data); + uint16_t entryCount(); + uint16_t operator[](uint16_t idx); + uint8_t* save(uint8_t* buffer); + uint8_t* restore(uint8_t* buffer); + + int32_t translateAsap(uint16_t asap); +protected: + void beforeStateChange(LoadState& newState); +private: + uint16_t* _tableData; +}; \ No newline at end of file diff --git a/bau.cpp b/bau.cpp new file mode 100644 index 0000000..5a4d380 --- /dev/null +++ b/bau.cpp @@ -0,0 +1,238 @@ +#include "bau.h" + +void BusAccessUnit::groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, bool status) +{ +} + +void BusAccessUnit::groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType) +{ +} + +void BusAccessUnit::groupValueReadResponseConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopTtype, uint8_t * data, uint8_t dataLength, bool status) +{ +} + +void BusAccessUnit::groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength) +{ +} + +void BusAccessUnit::groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength, bool status) +{ +} + +void BusAccessUnit::groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength) +{ +} + +void BusAccessUnit::individualAddressWriteLocalConfirm(AckType ack, HopCountType hopType, uint16_t newaddress, bool status) +{ +} + +void BusAccessUnit::individualAddressWriteIndication(HopCountType hopType, uint16_t newaddress) +{ +} + +void BusAccessUnit::individualAddressReadLocalConfirm(AckType ack, HopCountType hopType, bool status) +{ +} + +void BusAccessUnit::individualAddressReadIndication(HopCountType hopType) +{ +} + +void BusAccessUnit::individualAddressReadResponseConfirm(AckType ack, HopCountType hopType, bool status) +{ +} + +void BusAccessUnit::individualAddressReadAppLayerConfirm(HopCountType hopType, uint16_t individualAddress) +{ +} + +void BusAccessUnit::individualAddressSerialNumberReadLocalConfirm(AckType ack, HopCountType hopType, uint8_t * serialNumber, bool status) +{ +} + +void BusAccessUnit::individualAddressSerialNumberReadIndication(HopCountType hopType, uint8_t * serialNumber) +{ +} + +void BusAccessUnit::individualAddressSerialNumberReadResponseConfirm(AckType ack, HopCountType hopType, uint8_t * serialNumber, uint16_t domainAddress, bool status) +{ +} + +void BusAccessUnit::individualAddressSerialNumberReadAppLayerConfirm(HopCountType hopType, uint8_t * serialNumber, uint16_t individualAddress, uint16_t domainAddress) +{ +} + +void BusAccessUnit::individualAddressSerialNumberWriteLocalConfirm(AckType ack, HopCountType hopType, uint8_t* serialNumber, uint16_t newaddress, bool status) +{ +} + +void BusAccessUnit::individualAddressSerialNumberWriteIndication(HopCountType hopType, uint8_t * serialNumber, uint16_t newaddress) +{ +} + +void BusAccessUnit::deviceDescriptorReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType, bool status) +{ +} + +void BusAccessUnit::deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType) +{ +} + +void BusAccessUnit::deviceDescriptorReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptor_type, + uint8_t * device_descriptor, bool status) +{ +} + +void BusAccessUnit::deviceDescriptorReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptortype, uint8_t * deviceDescriptor) +{ +} + +void BusAccessUnit::restartRequestLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, bool status) +{ +} + +void BusAccessUnit::restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap) +{ +} + +void BusAccessUnit::propertyValueReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, bool status) +{ +} + +void BusAccessUnit::propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex) +{ +} + +void BusAccessUnit::propertyValueReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, 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, 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, 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, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t * data, uint8_t length) +{ +} + +void BusAccessUnit::propertyDescriptionReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool status) +{ +} + +void BusAccessUnit::propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex) +{ +} + +void BusAccessUnit::propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, 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, 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, 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, uint8_t number, uint16_t memoryAddress, bool status) +{ +} + +void BusAccessUnit::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress) +{ +} + +void BusAccessUnit::memoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, uint8_t * data, bool status) +{ +} + +void BusAccessUnit::memoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, uint8_t * data) +{ +} + +void BusAccessUnit::memoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, uint8_t * data, bool status) +{ +} + +void BusAccessUnit::memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, uint8_t * data) +{ +} + +void BusAccessUnit::userMemoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, bool status) +{ +} + +void BusAccessUnit::userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress) +{ +} + +void BusAccessUnit::userMemoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t * memoryData, bool status) +{ +} + +void BusAccessUnit::userMemoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t * memoryData) +{ +} + +void BusAccessUnit::userMemoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t * memoryData, bool status) +{ +} + +void BusAccessUnit::userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t * memoryData) +{ +} + +void BusAccessUnit::userManufacturerInfoLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, bool status) +{ +} + +void BusAccessUnit::userManufacturerInfoIndication(Priority priority, HopCountType hopType, uint16_t asap) +{ +} + +void BusAccessUnit::userManufacturerInfoResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t * info, bool status) +{ +} + +void BusAccessUnit::userManufacturerInfoAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t * info) +{ +} + +void BusAccessUnit::authorizeLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint32_t key, bool status) +{ +} + +void BusAccessUnit::authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, uint32_t key) +{ +} + +void BusAccessUnit::authorizeResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, bool status) +{ +} + +void BusAccessUnit::authorizeAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level) +{ +} + +void BusAccessUnit::keyWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, uint32_t key, bool status) +{ +} + +void BusAccessUnit::keyWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, uint32_t key) +{ +} + +void BusAccessUnit::keyWriteResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, bool status) +{ +} + +void BusAccessUnit::keyWriteAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level) +{ +} diff --git a/bau.h b/bau.h new file mode 100644 index 0000000..cb439a9 --- /dev/null +++ b/bau.h @@ -0,0 +1,110 @@ +#pragma once +#include +#include "knx_types.h" + +class BusAccessUnit +{ +public: + virtual void groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, bool status); + virtual void groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType); + virtual void groupValueReadResponseConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopTtype, + uint8_t* data, uint8_t dataLength, bool status); + virtual void groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, + uint8_t* data, uint8_t dataLength); + virtual void groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, + uint8_t* data, uint8_t dataLength, bool status); + virtual void groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, + uint8_t* data, uint8_t dataLength); + virtual void individualAddressWriteLocalConfirm(AckType ack, HopCountType hopType, + uint16_t newaddress, bool status); + virtual void individualAddressWriteIndication(HopCountType hopType, uint16_t newaddress); + virtual void individualAddressReadLocalConfirm(AckType ack, HopCountType hopType, bool status); + virtual void individualAddressReadIndication(HopCountType hopType); + virtual void individualAddressReadResponseConfirm(AckType ack, HopCountType hopType, bool status); + virtual void individualAddressReadAppLayerConfirm(HopCountType hopType, uint16_t individualAddress); + virtual void individualAddressSerialNumberReadLocalConfirm(AckType ack, HopCountType hopType, + uint8_t* serialNumber, bool status); + virtual void individualAddressSerialNumberReadIndication(HopCountType hopType, uint8_t* serialNumber); + virtual void individualAddressSerialNumberReadResponseConfirm(AckType ack, HopCountType hopType, + uint8_t* serialNumber, uint16_t domainAddress, bool status); + virtual void individualAddressSerialNumberReadAppLayerConfirm(HopCountType hopType, uint8_t* serialNumber, + uint16_t individualAddress, uint16_t domainAddress); + virtual void individualAddressSerialNumberWriteLocalConfirm(AckType ack, HopCountType hopType, uint8_t* serialNumber, + uint16_t newaddress, bool status); + virtual void individualAddressSerialNumberWriteIndication(HopCountType hopType, uint8_t* serialNumber, uint16_t newaddress); + virtual void deviceDescriptorReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t descriptorType, bool status); + virtual void deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType); + virtual void deviceDescriptorReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t descriptor_type, uint8_t* device_descriptor, bool status); + virtual void deviceDescriptorReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, + uint8_t descriptortype, uint8_t* deviceDescriptor); + virtual void restartRequestLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, bool status); + virtual void restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap); + virtual void propertyValueReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, bool status); + virtual void propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex); + virtual void propertyValueReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, 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, 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, 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, uint8_t objectIndex, + uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length); + virtual void propertyDescriptionReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool status); + virtual void propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, + uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex); + virtual void propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + 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, + 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, + 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, uint8_t number, + uint16_t memoryAddress, bool status); + virtual void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress); + virtual void memoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint16_t memoryAddress, uint8_t* data, bool status); + virtual void memoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint16_t memoryAddress, uint8_t* data); + virtual void memoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint16_t memoryAddress, uint8_t* data, bool status); + virtual void memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint16_t memoryAddress, uint8_t* data); + virtual void userMemoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint32_t memoryAddress, bool status); + virtual void userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint32_t memoryAddress); + virtual void userMemoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint32_t memoryAddress, uint8_t* memoryData, bool status); + virtual void userMemoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint32_t memoryAddress, uint8_t* memoryData); + virtual void userMemoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint32_t memoryAddress, uint8_t* memoryData, bool status); + virtual void userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint32_t memoryAddress, uint8_t* memoryData); + virtual void userManufacturerInfoLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, bool status); + virtual void userManufacturerInfoIndication(Priority priority, HopCountType hopType, uint16_t asap); + virtual void userManufacturerInfoResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, + uint8_t* info, bool status); + virtual void userManufacturerInfoAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, + uint8_t* info); + virtual void authorizeLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint32_t key, bool status); + virtual void authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, uint32_t key); + virtual void authorizeResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, + bool status); + virtual void authorizeAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level); + virtual void keyWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, + uint32_t key, bool status); + virtual void keyWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, + uint32_t key); + virtual void keyWriteResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, + bool status); + virtual void keyWriteAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level); +}; diff --git a/bau57B0.cpp b/bau57B0.cpp new file mode 100644 index 0000000..85c1d4a --- /dev/null +++ b/bau57B0.cpp @@ -0,0 +1,272 @@ +#include "bau57B0.h" +#include +#include + +using namespace std; + +Bau57B0::Bau57B0(Platform& platform): _memoryReference((uint8_t*)&_deviceObj), _memory(platform), _addrTable(_memoryReference), + _assocTable(_memoryReference), _groupObjTable(_memoryReference), _appProgram(_memoryReference), + _ipParameters(_deviceObj, _platform), _platform(platform), _appLayer(_assocTable, *this), + _transLayer(_appLayer, _addrTable, _platform), _netLayer(_transLayer), + _dlLayer(_deviceObj, _addrTable, _ipParameters, _netLayer, _platform) +{ + _appLayer.transportLayer(_transLayer); + _transLayer.networkLayer(_netLayer); + _netLayer.dataLinkLayer(_dlLayer); + _memory.addSaveRestore(&_deviceObj); + _memory.addSaveRestore(&_ipParameters); + _memory.addSaveRestore(&_appProgram); + _memory.addSaveRestore(&_addrTable); + _memory.addSaveRestore(&_assocTable); + _memory.addSaveRestore(&_groupObjTable); + _memory.readMemory(); + _dlLayer.enabled(true); +} + +void Bau57B0::loop() +{ + _dlLayer.loop(); + _transLayer.loop(); + sendNextGroupTelegram(); +} + +void Bau57B0::sendNextGroupTelegram() +{ + static uint16_t startIdx = 1; + + GroupObjectTableObject& table = _groupObjTable; + uint16_t objCount = table.entryCount(); + + for (uint16_t asap = startIdx; asap < objCount; asap++) + { + GroupObject& go = table.get(asap); + + ComFlag flag = go.commFlag(); + if (flag != ReadRequest && flag != WriteRequest) + continue; + + if(!go.communicationEnable() || ! go.transmitEnable()) + continue; + + if (flag == WriteRequest) + { + uint8_t* data = go.valueRef(); + _appLayer.groupValueWriteRequest(AckRequested, asap, go.priority(), NetworkLayerParameter, data, + go.sizeInTelegram()); + } + else + { + _appLayer.groupValueReadRequest(AckRequested, asap, go.priority(), NetworkLayerParameter); + } + + go.commFlag(Transmitting); + + startIdx = asap + 1; + return; + } + + startIdx = 1; +} + +void Bau57B0::updateGroupObject(GroupObject & go, uint8_t * data, uint8_t length) +{ + uint8_t* goData = go.valueRef(); + if (length != go.valueSize()) + { + go.commFlag(Error); + return; + } + + memcpy(goData, data, length); + + go.commFlag(cfUpdate); + if (go.updateHandler) + go.updateHandler(go); +} + +DeviceObject& Bau57B0::deviceObject() +{ + return _deviceObj; +} + +GroupObjectTableObject& Bau57B0::groupObjectTable() +{ + return _groupObjTable; +} + +ApplicationProgramObject& Bau57B0::parameters() +{ + return _appProgram; +} + +bool Bau57B0::configured() +{ + return _groupObjTable.loadState() == LS_LOADED + && _addrTable.loadState() == LS_LOADED + && _assocTable.loadState() == LS_LOADED + && _appProgram.loadState() == LS_LOADED; +} + +void Bau57B0::memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint16_t memoryAddress, uint8_t * data) +{ + memcpy(_memoryReference + memoryAddress, data, number); + _memory.memoryModified(); + + if (_deviceObj.verifyMode()) + memoryReadIndication(priority, hopType, asap, number, memoryAddress); +} + +void Bau57B0::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint16_t memoryAddress) +{ + _appLayer.memoryReadResponse(AckRequested, priority, hopType, asap, number, memoryAddress, + _memoryReference + memoryAddress); +} + +void Bau57B0::deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType) +{ + if (descriptorType != 0) + descriptorType = 0x3f; + + uint8_t descriptor[] = { 0x57, 0xb0 }; + + _appLayer.deviceDescriptorReadResponse(AckRequested, priority, hopType, asap, descriptorType, descriptor); +} + +void Bau57B0::restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap) +{ + // for platforms that don't really restart + _deviceObj.progMode(false); + + // Flush the EEPROM before resetting + _memory.writeMemory(); + _platform.restart(); +} + +void Bau57B0::authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, uint32_t key) +{ + _appLayer.authorizeResponse(AckRequested, priority, hopType, asap, 0); +} + +void Bau57B0::userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress) +{ + _appLayer.userMemoryReadResponse(AckRequested, priority, hopType, asap, number, memoryAddress, + _memoryReference + memoryAddress); +} + +void Bau57B0::userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t* data) +{ + memcpy(_memoryReference + memoryAddress, data, number); + _memory.memoryModified(); + + if (_deviceObj.verifyMode()) + userMemoryReadIndication(priority, hopType, asap, number, memoryAddress); +} + +void Bau57B0::propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + uint8_t propertyId, uint8_t propertyIndex) +{ + // TODO: reply correctly + _appLayer.propertyDescriptionReadResponse(AckRequested, priority, hopType, asap, objectIndex, propertyId, propertyIndex, + false, 0, 0, 0); +} + +void Bau57B0::propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) +{ + InterfaceObject& obj = getInterfaceObject(objectIndex); + obj.writeProperty((PropertyID)propertyId, startIndex, data, numberOfElements); + propertyValueReadIndication(priority, hopType, asap, objectIndex, propertyId, numberOfElements, startIndex); +} + +void Bau57B0::propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex) +{ + InterfaceObject& obj = getInterfaceObject(objectIndex); + uint8_t elementSize = obj.propertySize((PropertyID)propertyId); + uint8_t size = elementSize * numberOfElements; + uint8_t data[size]; + obj.readProperty((PropertyID)propertyId, startIndex, numberOfElements, data); + _appLayer.propertyValueReadResponse(AckRequested, priority, hopType, asap, objectIndex, propertyId, numberOfElements, + startIndex, data, size); +} + +void Bau57B0::individualAddressReadIndication(HopCountType hopType) +{ + if (_deviceObj.progMode()) + _appLayer.individualAddressReadResponse(AckRequested, hopType); +} + +void Bau57B0::individualAddressWriteIndication(HopCountType hopType, uint16_t newaddress) +{ + if (_deviceObj.progMode()) + _deviceObj.induvidualAddress(newaddress); +} + +void Bau57B0::groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength, bool status) +{ + GroupObject& go = _groupObjTable.get(asap); + if (status) + go.commFlag(Ok); + else + go.commFlag(Error); +} + +void Bau57B0::groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, bool status) +{ + GroupObject& go = _groupObjTable.get(asap); + if (status) + go.commFlag(Ok); + else + go.commFlag(Error); +} + +void Bau57B0::groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType) +{ + GroupObject& go = _groupObjTable.get(asap); + uint8_t* data = go.valueRef(); + _appLayer.groupValueReadResponse(AckRequested, asap, priority, hopType, data, go.sizeInTelegram()); +} + +void Bau57B0::groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, + uint8_t dataLength) +{ + GroupObject& go = _groupObjTable.get(asap); + + if (!go.communicationEnable() || !go.responseUpdateEnable()) + return; + + updateGroupObject(go, data, dataLength); +} + +void Bau57B0::groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength) +{ + GroupObject& go = _groupObjTable.get(asap); + + if (!go.communicationEnable() || !go.writeEnable()) + return; + + updateGroupObject(go, data, dataLength); +} + +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: + return _ipParameters; + default: + return _deviceObj; + } +} \ No newline at end of file diff --git a/bau57B0.h b/bau57B0.h new file mode 100644 index 0000000..1885e5d --- /dev/null +++ b/bau57B0.h @@ -0,0 +1,74 @@ +#pragma once + +#include "bau.h" +#include "device_object.h" +#include "address_table_object.h" +#include "association_table_object.h" +#include "group_object_table_object.h" +#include "application_program_object.h" +#include "ip_parameter_object.h" +#include "application_layer.h" +#include "transport_layer.h" +#include "network_layer.h" +#include "data_link_layer.h" +#include "platform.h" +#include "memory.h" + +class Bau57B0: protected BusAccessUnit +{ + using BusAccessUnit::memoryReadIndication; +public: + Bau57B0(Platform& platform); + void loop(); + DeviceObject& deviceObject(); + GroupObjectTableObject& groupObjectTable(); + ApplicationProgramObject& parameters(); + bool configured(); +protected: + void memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint16_t memoryAddress, uint8_t* data) override; + void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint16_t memoryAddress) override; + void deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType); + void restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap); + void authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, uint32_t key); + void userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress); + void userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, + uint32_t memoryAddress, uint8_t* memoryData); + void propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + uint8_t propertyId, uint8_t propertyIndex); + void propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length); + void propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, + uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex); + void individualAddressReadIndication(HopCountType hopType); + void individualAddressWriteIndication(HopCountType hopType, uint16_t newaddress); + void groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, + uint8_t* data, uint8_t dataLength, bool status); + void groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, bool status); + void groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType); + void groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, + uint8_t* data, uint8_t dataLength); + void groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, + uint8_t* data, uint8_t dataLength); + + InterfaceObject& getInterfaceObject(uint8_t idx); + void sendNextGroupTelegram(); + void updateGroupObject(GroupObject& go, uint8_t* data, uint8_t length); +private: + DeviceObject _deviceObj; + // pointer to first private variable as reference to memory read/write commands + uint8_t* _memoryReference; + Memory _memory; + AddressTableObject _addrTable; + AssociationTableObject _assocTable; + GroupObjectTableObject _groupObjTable; + ApplicationProgramObject _appProgram; + IpParameterObject _ipParameters; + Platform& _platform; + ApplicationLayer _appLayer; + TransportLayer _transLayer; + NetworkLayer _netLayer; + DataLinkLayer _dlLayer; + +}; \ No newline at end of file diff --git a/bits.cpp b/bits.cpp new file mode 100644 index 0000000..3941ab5 --- /dev/null +++ b/bits.cpp @@ -0,0 +1,75 @@ +#include "bits.h" + +uint8_t* popByte(uint8_t& b, uint8_t* data) +{ + b = *data; + data += 1; + return data; +} + +uint8_t* popWord(uint16_t& w, uint8_t* data) +{ + w = getWord(data); + data += 2; + return data; +} + +uint8_t* popInt(uint32_t& i, uint8_t* data) +{ + i = getInt(data); + data += 4; + return data; +} + +uint8_t* popByteArray(uint8_t* dst, uint32_t size, 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(uint8_t* data) +{ + return (data[0] << 8) + data[1];; +} + +uint32_t getInt(uint8_t * data) +{ + return (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3];; +} diff --git a/bits.h b/bits.h new file mode 100644 index 0000000..73583e1 --- /dev/null +++ b/bits.h @@ -0,0 +1,133 @@ +/* + * bits.h - Bit and uint8_t manipulation functions. + * + * Copyright (c) 2014 Stefan Taferner + * + * 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 +/** + * Compute the value of the specified bit. + * + * @param bitno - the number of the bit (0, 1, 2... 31) + */ +#define bit(bitno) (1UL << (bitno)) + +/** + * Clear the bit of a number. The number can be + * any integer (uint8_t, short, uint32_t, long). + * + * @param val - the number from which to clear the bit + * @param bitno - the number of the bit (0, 1, 2... 31) + */ +#define bitClear(val, bitno) ((val) &= ~(1UL << (bitno))) + +/** + * Set the bit of a number. The number can be + * any integer (uint8_t, short, uint32_t, long). + * + * @param val - the number from which to set the bit + * @param bitno - the number of the bit (0, 1, 2... 31) + */ +#define bitSet(val, bitno) ((val) |= 1UL << (bitno)) + +/** + * Write the value of a bit of a number. + * + * @param val - the number from which to write the bit + * @param bitno - the number of the bit (0, 1, 2... 31) + * @param b - the bit value (0 or 1) + */ +#define bitWrite(val, bitno, b) ((b) ? bitSet(val, bitno) : bitClear(val, bitno)) + +/** + * Read the value of a bit of a number. The number can be + * any integer (uint8_t, short, uint32_t, long). + * + * @param val - the number from which to get the bit + * @param bitno - the number of the bit (0, 1, 2... 31) + * @return The value of the bit (0 or 1). + */ +#define bitRead(val, bitno) (((val) >> (bitno)) & 1) + +/** + * Extract the lowest (rightmost) uint8_t of a number. The number can be + * any integer (uint8_t, short, uint32_t, long). + * + * @param val - the value to extract the lowest uint8_t. + * @return The extracted uint8_t (0..255) + */ +#define lowByte(val) ((val) & 255) + +/** + * Extract the highest (leftmost) uint8_t of a number. The number can be + * any integer (uint8_t, short, uint32_t, long). + * + * @param val - the value to extract the highest uint8_t. + * @return The extracted uint8_t (0..255) + */ +#define highByte(val) (((val) >> ((sizeof(val) - 1) << 3)) & 255) + +/** + * Combine two bytes to a 16 bit uint16_t. + * + * @param high - the high uint8_t. + * @param low - the low uint8_t. + * @return The bytes combined as uint16_t. + */ +uint16_t makeWord(uint8_t high, uint8_t low); + +/** + * Reverse the uint8_t order of an integer. + * + * @param val - the value to reverse. + * @return The value with reversed uint8_t order. + */ + uint32_t reverseByteOrder(uint32_t val); + +/** + * Reverse the uint8_t order of a short integer. + * + * @param val - the value to reverse. + * @return The value with reversed uint8_t order. + */ + uint16_t reverseByteOrder(uint16_t val); + + +// +// Inline functions +// + +inline uint16_t makeWord(uint8_t high, uint8_t low) +{ + return (high << 8) | low; +} + +inline uint32_t reverseByteOrder(uint32_t val) +{ + uint32_t swapped = ((val >> 24) & 0xff) | // move uint8_t 3 to uint8_t 0 + ((val << 8) & 0xff0000) | // move uint8_t 1 to uint8_t 2 + ((val >> 8) & 0xff00) | // move uint8_t 2 to uint8_t 1 + ((val << 24) & 0xff000000); // uint8_t 0 to uint8_t 3 + return swapped;//__REV(val); +} + +inline uint16_t reverseByteOrder(uint16_t val) +{ + uint16_t swapped = (val >> 8) | (val << 8); + return swapped; +} + +uint8_t* popByte(uint8_t& b, uint8_t* data); +uint8_t* popWord(uint16_t& w, uint8_t* data); +uint8_t* popInt(uint32_t& i, uint8_t* data); +uint8_t* popByteArray(uint8_t* dst, uint32_t size, 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(uint8_t* data); +uint32_t getInt(uint8_t* data); \ No newline at end of file diff --git a/cemi_frame.cpp b/cemi_frame.cpp new file mode 100644 index 0000000..e50d90d --- /dev/null +++ b/cemi_frame.cpp @@ -0,0 +1,195 @@ +#include "cemi_frame.h" +#include "bits.h" +#include "string.h" +#include + +CemiFrame::CemiFrame(uint8_t* data, uint16_t length): _npdu(data + NPDU_LPDU_DIFF, *this), + _tpdu(data + TPDU_LPDU_DIFF, *this), _apdu(data + APDU_LPDU_DIFF, *this) +{ + _data = data; + _ctrl1 = data + data[1] + 2; + _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 + 2; + _length = 0; + + memset(_data, 0, apduLength + APDU_LPDU_DIFF); + _ctrl1[0] |= Broadcast; + _npdu.octetCount(apduLength); +} + +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 + 2; + _length = other._length; + + memcpy(_data, other._data, other.totalLenght()); +} + +CemiFrame& CemiFrame::operator=(CemiFrame other) +{ + _length = other._length; + _data = buffer; + _ctrl1 = _data + 2; + 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 +{ + uint16_t tmp = + _npdu.length() + NPDU_LPDU_DIFF; + return tmp; +} + +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] & RepititionAllowed); +} + +void CemiFrame::repetition(Repetition rep) +{ + _ctrl1[0] &= ~RepititionAllowed; + _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; +} + +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); +} + +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[1 + _data[1] + NPDU_LPDU_DIFF]; + + if (_length != 0 && _length != (addInfoLen + apduLen + NPDU_LPDU_DIFF + 2)) + 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) + ) + return false; + + return true; +} diff --git a/cemi_frame.h b/cemi_frame.h new file mode 100644 index 0000000..2cb3807 --- /dev/null +++ b/cemi_frame.h @@ -0,0 +1,60 @@ +#pragma once + +#include "knx_types.h" +#include "stdint.h" +#include "npdu.h" +#include "tpdu.h" +#include "apdu.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) + +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; + + 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); + 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); + + NPDU& npdu(); + TPDU& tpdu(); + APDU& apdu(); + + bool valid() const; +private: + uint8_t buffer[0xff + NPDU_LPDU_DIFF]; //only valid of add info is zero + uint8_t* _data; + uint8_t* _ctrl1; + NPDU _npdu; + TPDU _tpdu; + APDU _apdu; + uint16_t _length; // only set if created from byte array +}; \ No newline at end of file diff --git a/data_link_layer.cpp b/data_link_layer.cpp new file mode 100644 index 0000000..61784bc --- /dev/null +++ b/data_link_layer.cpp @@ -0,0 +1,199 @@ +/* + * bus.cpp - Low level EIB bus access. + * + * Copyright (c) 2014 Stefan Taferner + * + * 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 "data_link_layer.h" + +#include "bits.h" +#include "platform.h" +#include "device_object.h" +#include "address_table_object.h" +#include "cemi_frame.h" + +#include +#include + +#define KNXIP_HEADER_LEN 0x6 +#define KNXIP_PROTOCOL_VERSION 0x10 + +#define ROUTING_INDICATION 0x0530 + +#define KNXIP_MULTICAST_PORT 3671 +#define MIN_LEN_CEMI 10 + +#ifdef DUMP_TELEGRAMS +unsigned char telBuffer[32]; +uint32_t telLength = 0; +#endif + +DataLinkLayer::DataLinkLayer(DeviceObject& devObj, AddressTableObject& addrTab, IpParameterObject& ipParam, + NetworkLayer& layer, Platform& platform) : + _deviceObject(devObj), _groupAddressTable(addrTab), _ipParameters(ipParam), _networkLayer(layer), _platform(platform) +{ +} + +void DataLinkLayer::dataRequest(AckType ack, AddressType addrType, uint16_t destinationAddr, FrameFormat format, Priority priority, NPDU& npdu) +{ + bool success = sendPacket(npdu, ack, destinationAddr, addrType, format, priority); + _networkLayer.dataConfirm(ack, addrType, destinationAddr, format, priority, npdu.frame().sourceAddress(), npdu, success); +} + +void DataLinkLayer::systemBroadcastRequest(AckType ack, FrameFormat format, Priority priority, NPDU& npdu) +{ + bool success = sendPacket(npdu, ack, 0, GroupAddress, format, priority); + _networkLayer.systemBroadcastConfirm(ack, format, priority, npdu.frame().sourceAddress(), npdu, success); +} + +bool DataLinkLayer::sendPacket(NPDU &npdu, AckType ack, uint16_t destinationAddr, AddressType addrType, FrameFormat format, Priority priority) +{ + CemiFrame& frame = npdu.frame(); + frame.messageCode(L_data_ind); + frame.destinationAddress(destinationAddr); + frame.sourceAddress(_deviceObject.induvidualAddress()); + frame.addressType(addrType); + frame.priority(priority); + frame.repetition(RepititionAllowed); + + if (npdu.octetCount() <= 15) + frame.frameType(StandardFrame); + else + frame.frameType(format); + + + if (!frame.valid()) + { + printf("invalid frame\n"); + return false; + } + + + //if (frame.npdu().octetCount() > 0) + //{ + // print.print("-> DLL "); + // frame.apdu().printPDU(); + //} + + + uint16_t length = frame.totalLenght() + KNXIP_HEADER_LEN; + uint8_t* buffer = new uint8_t[length]; + buffer[0] = KNXIP_HEADER_LEN; + buffer[1] = KNXIP_PROTOCOL_VERSION; + pushWord(ROUTING_INDICATION, buffer + 2); + pushWord(length, buffer + 4); + + memcpy(buffer + KNXIP_HEADER_LEN, frame._data, frame.totalLenght()); + + bool success = sendBytes(buffer, length); + // only send 50 packet per second: see KNX 3.2.6 p.6 + _platform.mdelay(20); + delete[] buffer; + return success; +} + +void DataLinkLayer::loop() +{ + if (!_enabled) + return; + + uint8_t buffer[512]; + int len = _platform.readBytes(buffer, 512); + if (len <= 0) + return; + + if (len < KNXIP_HEADER_LEN) + return; + + if (buffer[0] != KNXIP_HEADER_LEN + || buffer[1] != KNXIP_PROTOCOL_VERSION) + return; + + uint16_t code; + popWord(code, buffer + 2); + if (code != ROUTING_INDICATION) // only routing indication for now + return; + + if (len < MIN_LEN_CEMI) + return; + + //TODO: Check correct length (additions Info + apdu length) + CemiFrame frame(buffer + KNXIP_HEADER_LEN, len - KNXIP_HEADER_LEN); + 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.induvidualAddress(); + + if (source == ownAddr) + _deviceObject.induvidualAddressDuplication(true); + + if (addrType == GroupAddress && destination == 0) + _networkLayer.systemBroadcastIndication(ack, type, npdu, priority, source); + else + { + if (addrType == InduvidualAddress && destination != _deviceObject.induvidualAddress()) + return; + + if (addrType == GroupAddress && !_groupAddressTable.contains(destination)) + return; + + //if (frame.npdu().octetCount() > 0) + //{ + // print.print("<- DLL "); + // frame.apdu().printPDU(); + //} + + _networkLayer.dataIndication(ack, addrType, destination, type, npdu, priority, source); + } +} + +void DataLinkLayer::enabled(bool value) +{ + if (value && !_enabled) + { + _platform.setupMultiCast(_ipParameters.multicastAddress(), KNXIP_MULTICAST_PORT); + _enabled = true; + return; + } + + if(!value && _enabled) + { + _platform.closeMultiCast(); + _enabled = false; + return; + } +} + +bool DataLinkLayer::enabled() const +{ + return _enabled; +} + + +bool DataLinkLayer::sendBytes(uint8_t* bytes, uint16_t length) +{ + if (!_enabled) + return false; + +#ifdef DUMP_TELEGRAMS_ + { + print.print("QSD: "); + for (uint32_t i = 0; i <= length; ++i) + { + if (i) print.print(" "); + print.print(bytes[i], HEX, 2); + } + print.println(); + } +#endif + + return _platform.sendBytes(bytes, length); +} \ No newline at end of file diff --git a/data_link_layer.h b/data_link_layer.h new file mode 100644 index 0000000..ce1ae3a --- /dev/null +++ b/data_link_layer.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include "device_object.h" +#include "address_table_object.h" +#include "ip_parameter_object.h" +#include "knx_types.h" +#include "network_layer.h" + +class DataLinkLayer +{ +public: + DataLinkLayer(DeviceObject& devObj, AddressTableObject& addrTab, IpParameterObject& ipParam, NetworkLayer& layer, + Platform& platform); + + // from network layer + void dataRequest(AckType ack, AddressType addrType, uint16_t destinationAddr, FrameFormat format, + Priority priority, NPDU& npdu); + void systemBroadcastRequest(AckType ack, FrameFormat format, Priority priority, NPDU& npdu); + void loop(); + void enabled(bool value); + bool enabled() const; +private: + bool _enabled = false; + bool sendPacket(NPDU &npdu, AckType ack, uint16_t destinationAddr, AddressType addrType, FrameFormat format, Priority priority); + bool sendBytes(uint8_t* buffer, uint16_t length); + + DeviceObject& _deviceObject; + AddressTableObject& _groupAddressTable; + IpParameterObject& _ipParameters; + NetworkLayer& _networkLayer; + Platform& _platform; +}; \ No newline at end of file diff --git a/datapoint_types.cpp b/datapoint_types.cpp new file mode 100644 index 0000000..ef8b5fb --- /dev/null +++ b/datapoint_types.cpp @@ -0,0 +1,63 @@ +/* + * datapoint_types.h - Conversion functions for datapoint types. + * + * Copyright (c) 2014 Stefan Taferner + * + * 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 + +// 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; +} diff --git a/datapoint_types.h b/datapoint_types.h new file mode 100644 index 0000000..1d4ff09 --- /dev/null +++ b/datapoint_types.h @@ -0,0 +1,37 @@ +/* + * datapoint_types.h - Conversion functions for datapoint types. + * + * Copyright (c) 2014 Stefan Taferner + * + * 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 + +/** + * 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); \ No newline at end of file diff --git a/device_object.cpp b/device_object.cpp new file mode 100644 index 0000000..5e6286b --- /dev/null +++ b/device_object.cpp @@ -0,0 +1,240 @@ +#include +#include "device_object.h" +#include "bits.h" + +void DeviceObject::readProperty(PropertyID propertyId, uint32_t start, uint32_t count, uint8_t* data) +{ + switch (propertyId) + { + case PID_OBJECT_TYPE: + pushWord(OT_DEVICE, data); + break; + case PID_SERIAL_NUMBER: + pushWord(_manufacturerId, data); + pushInt(_bauNumber, data); + break; + case PID_MANUFACTURER_ID: + pushWord(_manufacturerId, data); + break; + case PID_DEVICE_CONTROL: + *data = _deviceControl; + break; + case PID_ORDER_INFO: + pushByteArray((uint8_t*)_orderNumber, 10, data); + break; + case PID_HARDWARE_TYPE: + pushByteArray((uint8_t*)_hardwareType, 6, data); + break; + case PID_VERSION: + pushWord(_version, data); + break; + case PID_ROUTING_COUNT: + *data = _routingCount; + break; + case PID_PROG_MODE: + *data = _prgMode; + break; + case PID_MAX_APDU_LENGTH: + *data = 15; + break; + case PID_SUBNET_ADDR: + *data = ((_ownAddress >> 8) & 0xff); + break; + case PID_DEVICE_ADDR: + *data = (_ownAddress & 0xff); + break; + case PID_IO_LIST: + { + uint32_t ifObjs[] = { + 6, // length + OT_DEVICE, OT_ADDR_TABLE, OT_ASSOC_TABLE, OT_GRP_OBJ_TABLE, OT_APPLICATION_PROG, OT_IP_PARAMETER}; + + for (uint32_t i = start; i < (ifObjs[0] + 1) && i < count; i++) + pushInt(ifObjs[i], data); + + break; + } + case PID_DEVICE_DESCRIPTOR: + data[0] = 0x57; + data[1] = 0xB0; + break; + } +} + +void DeviceObject::writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count) +{ + switch (id) + { + case PID_DEVICE_CONTROL: + _deviceControl = data[0]; + break; + case PID_ROUTING_COUNT: + _routingCount = data[0]; + break; + case PID_PROG_MODE: + _prgMode = data[0]; + break; + } +} + +uint8_t DeviceObject::propertySize(PropertyID id) +{ + switch (id) + { + case PID_DEVICE_CONTROL: + return 1; + case PID_ROUTING_COUNT: + return 1; + case PID_PROG_MODE: + return 1; + } + return 0; +} + +uint8_t* DeviceObject::save(uint8_t* buffer) +{ + buffer = pushByte(_deviceControl, buffer); + buffer = pushByte(_routingCount, buffer); + buffer = pushWord(_ownAddress, buffer); + return buffer; +} + +uint8_t* DeviceObject::restore(uint8_t* buffer) +{ + buffer = popByte(_deviceControl, buffer); + buffer = popByte(_routingCount, buffer); + buffer = popWord(_ownAddress, buffer); + _prgMode = 0; + return buffer; +} + +uint16_t DeviceObject::induvidualAddress() +{ + return _ownAddress; +} + +void DeviceObject::induvidualAddress(uint16_t value) +{ + _ownAddress = value; +} + +#define USER_STOPPED 0x1 +#define OWN_ADDR_DUPL 0x2 +#define VERIFY_MODE 0x4 +#define SAFE_STATE 0x8 + + +bool DeviceObject::userStopped() +{ + return (_deviceControl & USER_STOPPED) > 0; +} + +void DeviceObject::userStopped(bool value) +{ + if (value) + _deviceControl |= USER_STOPPED; + else + _deviceControl &= ~USER_STOPPED; +} + +bool DeviceObject::induvidualAddressDuplication() +{ + return (_deviceControl & OWN_ADDR_DUPL) > 0; +} + +void DeviceObject::induvidualAddressDuplication(bool value) +{ + if (value) + _deviceControl |= OWN_ADDR_DUPL; + else + _deviceControl &= ~OWN_ADDR_DUPL; +} + +bool DeviceObject::verifyMode() +{ + return (_deviceControl & VERIFY_MODE) > 0; +} + +void DeviceObject::verifyMode(bool value) +{ + if (value) + _deviceControl |= VERIFY_MODE; + else + _deviceControl &= ~VERIFY_MODE; +} + +bool DeviceObject::safeState() +{ + return (_deviceControl & SAFE_STATE) > 0; +} + +void DeviceObject::safeState(bool value) +{ + if (value) + _deviceControl |= SAFE_STATE; + else + _deviceControl &= ~SAFE_STATE; +} + +bool DeviceObject::progMode() +{ + return _prgMode == 1; +} + +void DeviceObject::progMode(bool value) +{ + if (value) + _prgMode = 1; + else + _prgMode = 0; +} + +uint16_t DeviceObject::manufacturerId() +{ + return _manufacturerId; +} + +void DeviceObject::manufacturerId(uint16_t value) +{ + _manufacturerId = value; +} + +uint32_t DeviceObject::bauNumber() +{ + return _bauNumber; +} + +void DeviceObject::bauNumber(uint32_t value) +{ + _bauNumber = value; +} + +const char* DeviceObject::orderNumber() +{ + return _orderNumber; +} + +void DeviceObject::orderNumber(const char* value) +{ + strncpy(_orderNumber, value, 10); +} + +const uint8_t* DeviceObject::hardwareType() +{ + return _hardwareType; +} + +void DeviceObject::hardwareType(const uint8_t* value) +{ + pushByteArray(value, 6, _hardwareType); +} + +uint16_t DeviceObject::version() +{ + return _version; +} + +void DeviceObject::version(uint16_t value) +{ + _version = value; +} diff --git a/device_object.h b/device_object.h new file mode 100644 index 0000000..6270bbd --- /dev/null +++ b/device_object.h @@ -0,0 +1,46 @@ +#pragma once + +#include "interface_object.h" + +class DeviceObject: public InterfaceObject +{ +public: + void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data); + void writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count); + uint8_t propertySize(PropertyID id); + uint8_t* save(uint8_t* buffer); + uint8_t* restore(uint8_t* buffer); + + uint16_t induvidualAddress(); + void induvidualAddress(uint16_t value); + bool userStopped(); + void userStopped(bool value); + bool induvidualAddressDuplication(); + void induvidualAddressDuplication(bool value); + bool verifyMode(); + void verifyMode(bool value); + bool safeState(); + void safeState(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 char* orderNumber(); + void orderNumber(const char* value); + const uint8_t* hardwareType(); + void hardwareType(const uint8_t* value); + uint16_t version(); + void version(uint16_t value); +private: + uint8_t _deviceControl; + uint8_t _routingCount; + uint8_t _prgMode; + uint16_t _ownAddress; + uint16_t _manufacturerId; + uint32_t _bauNumber; + char _orderNumber[10]; + uint8_t _hardwareType[6]; + uint16_t _version; +}; \ No newline at end of file diff --git a/esp_platform.cpp b/esp_platform.cpp new file mode 100644 index 0000000..738f72d --- /dev/null +++ b/esp_platform.cpp @@ -0,0 +1,102 @@ +#include "esp_platform.h" +#include +#include +#include + +EspPlatform::EspPlatform() +{ +} + +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::millis() +{ + return millis(); +} + +void EspPlatform::mdelay(uint32_t millis) +{ + delay(millis); +} + +void EspPlatform::restart() +{ + ESP.restart(); +} + +void EspPlatform::fatalError() +{ + const int period = 200; + while (true) + { + if ((millis() % period) > (period / 2)) + digitalWrite(LED_BUILTIN, HIGH); + else + digitalWrite(LED_BUILTIN, LOW); + } +} + +void EspPlatform::setupMultiCast(uint32_t addr, uint16_t port) +{ + _mulitcastAddr = addr; + _mulitcastPort = port; + _udp.beginMulticast(WiFi.localIP(), addr, port); +} + +void EspPlatform::closeMultiCast() +{ + _udp.stop(); +} + +bool EspPlatform::sendBytes(uint8_t * buffer, uint16_t len) +{ + _udp.beginPacketMulticast(_mulitcastAddr, _mulitcastPort, WiFi.localIP()); + _udp.write(buffer, len); + _udp.endPacket(); + return true; +} + +int EspPlatform::readBytes(uint8_t * buffer, uint16_t maxLen) +{ + int len = _udp.parsePacket(); + if (len == 0) + return 0; + + if (len > maxLen) + { + printf("udp buffer to small. was %d, needed %d\n", maxLen, len); + fatalError(); + } + + _udp.read(buffer, len); + return len; +} + +uint8_t * EspPlatform::getEepromBuffer(uint16_t size) +{ + EEPROM.begin(size); + return EEPROM.getDataPtr(); +} + +void EspPlatform::commitToEeprom() +{ + EEPROM.commit(); +} diff --git a/esp_platform.h b/esp_platform.h new file mode 100644 index 0000000..1b113d0 --- /dev/null +++ b/esp_platform.h @@ -0,0 +1,36 @@ +#include "platform.h" +#include +#include + +class EspPlatform : public Platform +{ +public: + EspPlatform(); + + // ip stuff + uint32_t currentIpAddress(); + uint32_t currentSubnetMask(); + uint32_t currentDefaultGateway(); + void macAddress(uint8_t* addr); + + // basic stuff + uint32_t millis(); + void mdelay(uint32_t millis); + void restart(); + void fatalError(); + + //multicast + void setupMultiCast(uint32_t addr, uint16_t port); + void closeMultiCast(); + bool sendBytes(uint8_t* buffer, uint16_t len); + int readBytes(uint8_t* buffer, uint16_t maxLen); + + //memory + uint8_t* getEepromBuffer(uint16_t size); + void commitToEeprom(); +private: + uint32_t _mulitcastAddr; + uint16_t _mulitcastPort; + WiFiUDP _udp; +}; + diff --git a/group_object.cpp b/group_object.cpp new file mode 100644 index 0000000..4381b0c --- /dev/null +++ b/group_object.cpp @@ -0,0 +1,191 @@ +#include "group_object.h" +#include "bits.h" +#include "string.h" +#include "datapoint_types.h" +#include "group_object_table_object.h" + +GroupObject::GroupObject(uint8_t size) +{ + _data = new uint8_t[size]; + _commFlag = Ok; + _table = 0; + _dataLength = size; + updateHandler = 0; +} + +GroupObject::~GroupObject() +{ + delete[] _data; +} + +bool GroupObject::responseUpdateEnable() +{ + if (!_table) + return false; + + return bitRead(_table->_tableData[_asap], 15) > 0; +} + +bool GroupObject::transmitEnable() +{ + if (!_table) + return false; + + return bitRead(_table->_tableData[_asap], 14) > 0; +} + +bool GroupObject::valueReadOnInit() +{ + if (!_table) + return false; + + return bitRead(_table->_tableData[_asap], 13) > 0; +} + +bool GroupObject::writeEnable() +{ + if (!_table) + return false; + + return bitRead(_table->_tableData[_asap], 12) > 0; +} + +bool GroupObject::readEnable() +{ + if (!_table) + return false; + + return bitRead(_table->_tableData[_asap], 11) > 0; +} + +bool GroupObject::communicationEnable() +{ + if (!_table) + return false; + + return bitRead(_table->_tableData[_asap], 10) > 0; +} + + +Priority GroupObject::priority() +{ + if (!_table) + return LowPriority; + + return (Priority)((_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) +{ + 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; +} + +int32_t GroupObject::objectReadFloat() +{ + uint16_t dptValue = makeWord(_data[0], _data[1]); + return dptFromFloat(dptValue); +} + +bool GroupObject::objectReadBool() +{ + return _data[0] > 0; +} + +void GroupObject::requestObjectRead() +{ + _commFlag = ReadRequest; +} + +void GroupObject::objectWritten() +{ + _commFlag = WriteRequest; +} + + +void GroupObject::objectWriteFloat(int32_t value) +{ + uint16_t dptValue = dptToFloat(value); + pushWord(dptValue, _data); + objectWritten(); +} + + +void GroupObject::objectUpdateFloat(int32_t value) +{ + uint16_t dptValue = dptToFloat(value); + pushWord(dptValue, _data); + _commFlag = cfUpdate; +} + +size_t GroupObject::valueSize() +{ + return _dataLength; +} + +size_t GroupObject::sizeInTelegram() +{ + uint8_t code = lowByte(_table->_tableData[_asap]); + return asapValueSize(code); +} diff --git a/group_object.h b/group_object.h new file mode 100644 index 0000000..1f35b75 --- /dev/null +++ b/group_object.h @@ -0,0 +1,111 @@ +#pragma once + +#include +#include +#include "knx_types.h" + +class GroupObjectTableObject; + +enum ComFlag +{ + cfUpdate = 0, + ReadRequest = 1, + WriteRequest = 2, + Transmitting = 3, + Ok = 4, + Error = 5 +}; + +class GroupObject; +typedef void (*GroupObjectUpdatedHandler)(GroupObject& go); + +class GroupObject +{ + friend class GroupObjectTableObject; +public: + GroupObject(uint8_t size); + virtual ~GroupObject(); + // config flags from ETS + bool responseUpdateEnable(); + bool transmitEnable(); + bool valueReadOnInit(); + bool writeEnable(); + bool readEnable(); + bool communicationEnable(); + Priority priority(); + + ComFlag commFlag(); + void commFlag(ComFlag value); + + /** + * Get the float value from a communication object. Can be used for + * communication objects of type 2 uint8_t float (EIS5 / DPT9). The value is in + * 1/100 - a DPT9 value of 21.01 is returned as 2101. + * + * @return The value of the com-object in 1/100. INVALID_DPT_FLOAT is returned + * for the DPT9 "invalid data" value. + */ + int32_t objectReadFloat(); + bool objectReadBool(); + /** + * 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. + * You can cycle through all updated communication objects with nextUpdatedObject(). + * + * + * @see objectWritten() + */ + void requestObjectRead(); + /** + * Mark a communication object as written. Use this function if you directly change + * the value of a communication object without using objectWrite(). Calling this + * function triggers the sending of a write-group-value telegram. + * + * @see requestObjectRead() + */ + void objectWritten(); + + /** + * Set the value of a communication object. Calling this function triggers the + * sending of a write-group-value telegram. + * + * The communication object is a 2 uint8_t float (EIS5 / DPT9) object. The value is + * in 1/100, so a value of 2101 would set a DPT9 float value of 21.01. The valid + * range of the values is -671088.64 to 670760.96. + * + * @param value - the new value of the communication object in 1/100. + * Use INVALID_DPT_FLOAT for the DPT9 "invalid data" value. + */ + void objectWriteFloat(int32_t value); + + /** + * Set the value of a communication object and mark the communication object + * as updated. This does not trigger a write-group-value telegram. + * + * The communication object is a 2 uint8_t float (EIS5 / DPT9) object. The value + * is in 1/100, so a value of 2101 would set a DPT9 float value of 21.01. + * The possible range of the values is -671088.64 to 670760.96. + * + * @param objno - the ID of the communication object. + * @param value - the new value of the communication object in 1/100. + * Use INVALID_DPT_FLOAT for the DPT9 "invalid data" value. + */ + void objectUpdateFloat(int32_t value); + + size_t valueSize(); + size_t asapValueSize(uint8_t code); + size_t sizeInTelegram(); + uint8_t* valueRef(); + uint16_t asap(); + GroupObjectUpdatedHandler updateHandler; +private: + size_t goSize(); + uint16_t _asap; + ComFlag _commFlag; + uint8_t* _data; + uint8_t _dataLength; + GroupObjectTableObject* _table; +}; diff --git a/group_object_table_object.cpp b/group_object_table_object.cpp new file mode 100644 index 0000000..d9cc83c --- /dev/null +++ b/group_object_table_object.cpp @@ -0,0 +1,125 @@ +#include + +#include "group_object_table_object.h" +#include "group_object.h" +#include "bits.h" + +GroupObjectTableObject::GroupObjectTableObject(uint8_t* memoryReference): TableObject(memoryReference) +{ + _groupObjects = 0; + _groupObjectCount = 0; +} + +void GroupObjectTableObject::readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data) +{ + switch (id) + { + case PID_OBJECT_TYPE: + pushWord(OT_GRP_OBJ_TABLE, data); + break; + default: + TableObject::readProperty(id, start, count, data); + } +} + +uint16_t GroupObjectTableObject::entryCount() +{ + if (loadState() != LS_LOADED) + return 0; + + return _tableData[0]; +} + + + +GroupObject& GroupObjectTableObject::get(uint16_t asap) +{ + return _groupObjects[asap - 1]; +} + + +uint8_t* GroupObjectTableObject::save(uint8_t* buffer) +{ + return TableObject::save(buffer); +} + + +uint8_t* GroupObjectTableObject::restore(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() == cfUpdate) + { + 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) +{ + _groupObjects = objs; + _groupObjectCount = size; + initGroupObjects(); +} + +void GroupObjectTableObject::beforeStateChange(LoadState& newState) +{ + if (newState != LS_LOADED) + return; + + _tableData = (uint16_t*)_data; + uint16_t goCount = reverseByteOrder(_tableData[0]); + // big endian -> little endian + for (size_t i = 0; i <= goCount; i++) + _tableData[i] = reverseByteOrder(_tableData[i]); + + if (!initGroupObjects()) + { + newState = LS_ERROR; + TableObject::_errorCode = E_SOFTWARE_FAULT; + } +} + +bool GroupObjectTableObject::initGroupObjects() +{ + if (!_tableData) + return false; + + uint16_t goCount = _tableData[0]; + if (goCount != _groupObjectCount) + return false; + + for (uint16_t asap = 1; asap <= goCount; asap++) + { + GroupObject& go = _groupObjects[asap - 1]; + go._asap = asap; + go._table = this; + if (go._dataLength != go.goSize()) + return false; + } + + return true; +} diff --git a/group_object_table_object.h b/group_object_table_object.h new file mode 100644 index 0000000..4677ca1 --- /dev/null +++ b/group_object_table_object.h @@ -0,0 +1,27 @@ +#pragma once + +#include "table_object.h" +#include "group_object.h" + +class GroupObjectTableObject: public TableObject +{ + friend class GroupObject; + +public: + GroupObjectTableObject(uint8_t* memoryReference); + void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data); + uint16_t entryCount(); + GroupObject& get(uint16_t asap); + GroupObject& nextUpdatedObject(bool& valid); + void groupObjects(GroupObject* objs, uint16_t size); + + virtual uint8_t* save(uint8_t* buffer); + virtual uint8_t* restore(uint8_t* buffer); +protected: + virtual void beforeStateChange(LoadState& newState); +private: + bool initGroupObjects(); + uint16_t* _tableData = 0; + GroupObject* _groupObjects; + uint16_t _groupObjectCount; +}; \ No newline at end of file diff --git a/interface_object.h b/interface_object.h new file mode 100644 index 0000000..d433a7b --- /dev/null +++ b/interface_object.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include "property_types.h" +#include "save_restore.h" + +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 +}; + + +class InterfaceObject: public SaveRestore +{ +public: + virtual ~InterfaceObject() {} + virtual void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data) = 0; + virtual void writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count) = 0; + virtual uint8_t propertySize(PropertyID id) = 0; +protected: + +}; \ No newline at end of file diff --git a/ip_parameter_object.cpp b/ip_parameter_object.cpp new file mode 100644 index 0000000..d7d13e2 --- /dev/null +++ b/ip_parameter_object.cpp @@ -0,0 +1,298 @@ +#include "ip_parameter_object.h" +#include "device_object.h" +#include "platform.h" +#include "bits.h" + +//224.0.23.12 +#define DEFAULT_MULTICAST_ADDR 0xE000170C + +IpParameterObject::IpParameterObject(DeviceObject& deviceObject, Platform& platform): _deviceObject(deviceObject), + _platform(platform) +{} + +void IpParameterObject::readProperty(PropertyID propertyId, uint32_t start, uint32_t count, uint8_t* data) +{ + switch (propertyId) + { + case PID_LOAD_STATE_CONTROL: + data[0] = _state; + break; + case PID_OBJECT_TYPE: + pushWord(OT_IP_PARAMETER, data); + break; + case PID_PROJECT_INSTALLATION_ID: + pushWord(_projectInstallationId, data); + break; + case PID_KNX_INDIVIDUAL_ADDRESS: + pushWord(_deviceObject.induvidualAddress(), data); + break; + case PID_IP_ASSIGNMENT_METHOD: + data[0] = _ipAssignmentMethod; + break; + case PID_IP_CAPABILITIES: + data[0] = _ipCapabilities; + break; + case PID_CURRENT_IP_ADDRESS: + pushInt(_platform.currentIpAddress(), data); + break; + case PID_CURRENT_SUBNET_MASK: + pushInt(_platform.currentSubnetMask(), data); + break; + case PID_CURRENT_DEFAULT_GATEWAY: + pushInt(_platform.currentDefaultGateway(), data); + break; + case PID_IP_ADDRESS: + pushInt(_ipAddress, data); + break; + case PID_SUBNET_MASK: + pushInt(_subnetMask, data); + break; + case PID_DEFAULT_GATEWAY: + pushInt(_defaultGateway, data); + break; + case PID_MAC_ADDRESS: + { + uint8_t macAddr[6]; + _platform.macAddress(macAddr); + pushByteArray(macAddr, 6, data); + break; + } + case PID_SYSTEM_SETUP_MULTICAST_ADDRESS: + pushInt(DEFAULT_MULTICAST_ADDR, data); + break; + case PID_ROUTING_MULTICAST_ADDRESS: + pushInt(_multicastAddress, data); + break; + case PID_TTL: + data[0] = ttl(); + break; + case PID_KNXNETIP_DEVICE_CAPABILITIES: + data[0] = 0x1; + break; + case PID_FRIENDLY_NAME: + for (uint8_t i = start; i < start + count; i++) + data[i-start] = _friendlyName[i-1]; + break; + } +} + +void IpParameterObject::writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count) +{ + switch (id) + { + case PID_LOAD_STATE_CONTROL: + loadEvent(data); + break; + case PID_PROJECT_INSTALLATION_ID: + _projectInstallationId = getWord(data); + break; + case PID_KNX_INDIVIDUAL_ADDRESS: + _deviceObject.induvidualAddress(getWord(data)); + break; + case PID_IP_ASSIGNMENT_METHOD: + _ipAssignmentMethod = data[0]; + break; + case PID_IP_ADDRESS: + _ipAddress = getInt(data); + break; + case PID_SUBNET_MASK: + _subnetMask = getInt(data); + break; + case PID_DEFAULT_GATEWAY: + _defaultGateway = getInt(data); + break; + case PID_ROUTING_MULTICAST_ADDRESS: + _multicastAddress = getInt(data); + break; + case PID_TTL: + _ttl = data[0]; + break; + case PID_FRIENDLY_NAME: + for (uint8_t i = start; i < start + count; i++) + _friendlyName[i-1] = data[i - start]; + break; + } +} + +uint8_t IpParameterObject::propertySize(PropertyID id) +{ + switch (id) + { + case PID_PROJECT_INSTALLATION_ID: + return 2; + case PID_KNX_INDIVIDUAL_ADDRESS: + return 2; + case PID_IP_ASSIGNMENT_METHOD: + return 1; + case PID_IP_ADDRESS: + return 4; + case PID_SUBNET_MASK: + return 4; + case PID_DEFAULT_GATEWAY: + return 4; + case PID_ROUTING_MULTICAST_ADDRESS: + return 4; + case PID_TTL: + return 1; + case PID_FRIENDLY_NAME: + return 1; + } + return 0; +} + +uint8_t* IpParameterObject::save(uint8_t* buffer) +{ + buffer = pushWord(_projectInstallationId, buffer); + buffer = pushByte(_ipAssignmentMethod, buffer); + buffer = pushByte(_ipCapabilities, buffer); + buffer = pushInt(_ipAddress, buffer); + buffer = pushInt(_subnetMask, buffer); + buffer = pushInt(_defaultGateway, buffer); + buffer = pushInt(_multicastAddress, buffer); + buffer = pushByte(_ttl, buffer); + buffer = pushByteArray((uint8_t*)_friendlyName, 30, buffer); + + return buffer; +} + +uint8_t* IpParameterObject::restore(uint8_t* buffer) +{ + buffer = popWord(_projectInstallationId, buffer); + buffer = popByte(_ipAssignmentMethod, buffer); + buffer = popByte(_ipCapabilities, buffer); + buffer = popInt(_ipAddress, buffer); + buffer = popInt(_subnetMask, buffer); + buffer = popInt(_defaultGateway, buffer); + buffer = popInt(_multicastAddress, buffer); + buffer = popByte(_ttl, buffer); + buffer = popByteArray((uint8_t*)_friendlyName, 30, buffer); + + return buffer; +} + +uint32_t IpParameterObject::multicastAddress() const +{ + if (_multicastAddress == 0) + return DEFAULT_MULTICAST_ADDR; + + return _multicastAddress; +} + +void IpParameterObject::loadEvent(uint8_t* data) +{ + switch (_state) + { + case LS_UNLOADED: + loadEventUnloaded(data); + break; + case LS_LOADING: + loadEventLoading(data); + break; + case LS_LOADED: + loadEventLoaded(data); + break; + case LS_ERROR: + loadEventError(data); + break; + } +} + +void IpParameterObject::loadState(LoadState newState) +{ + if (newState == _state) + return; + //beforeStateChange(newState); + _state = newState; +} + +void IpParameterObject::loadEventUnloaded(uint8_t* data) +{ + uint8_t event = data[0]; + switch (event) + { + case LE_NOOP: + case LE_LOAD_COMPLETED: + case LE_ADDITIONAL_LOAD_CONTROLS: + case LE_UNLOAD: + break; + case LE_START_LOADING: + loadState(LS_LOADING); + break; + default: + loadState(LS_ERROR); + _errorCode = E_GOT_UNDEF_LOAD_CMD; + } +} + +void IpParameterObject::loadEventLoading(uint8_t* data) +{ + uint8_t event = data[0]; + switch (event) + { + case LE_NOOP: + case LE_START_LOADING: + break; + case LE_LOAD_COMPLETED: + loadState(LS_LOADED); + break; + case LE_UNLOAD: + loadState(LS_UNLOADED); + break; + case LE_ADDITIONAL_LOAD_CONTROLS: + additionalLoadControls(data); + break; + default: + loadState(LS_ERROR); + _errorCode = E_GOT_UNDEF_LOAD_CMD; + } +} + +void IpParameterObject::loadEventLoaded(uint8_t* data) +{ + uint8_t event = data[0]; + switch (event) + { + case LE_NOOP: + case LE_LOAD_COMPLETED: + break; + case LE_START_LOADING: + loadState(LS_LOADING); + break; + case LE_UNLOAD: + loadState(LS_UNLOADED); + break; + case LE_ADDITIONAL_LOAD_CONTROLS: + loadState(LS_ERROR); + _errorCode = E_INVALID_OPCODE; + break; + default: + loadState(LS_ERROR); + _errorCode = E_GOT_UNDEF_LOAD_CMD; + } +} + +void IpParameterObject::loadEventError(uint8_t* data) +{ + uint8_t event = data[0]; + switch (event) + { + case LE_NOOP: + case LE_LOAD_COMPLETED: + case LE_ADDITIONAL_LOAD_CONTROLS: + case LE_START_LOADING: + break; + case LE_UNLOAD: + loadState(LS_UNLOADED); + break; + default: + loadState(LS_ERROR); + _errorCode = E_GOT_UNDEF_LOAD_CMD; + } +} + +void IpParameterObject::additionalLoadControls(uint8_t* data) +{ + loadState(LS_ERROR); + _errorCode = E_INVALID_OPCODE; + return; +} \ No newline at end of file diff --git a/ip_parameter_object.h b/ip_parameter_object.h new file mode 100644 index 0000000..2b288d3 --- /dev/null +++ b/ip_parameter_object.h @@ -0,0 +1,42 @@ +#pragma once + +#include "interface_object.h" +#include "device_object.h" +#include "platform.h" + +class IpParameterObject: public InterfaceObject +{ +public: + IpParameterObject(DeviceObject& deviceObject, Platform& platform); + void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data); + void writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count); + uint8_t propertySize(PropertyID id); + + uint8_t* save(uint8_t* buffer); + uint8_t* restore(uint8_t* buffer); + + uint32_t multicastAddress() const; + uint8_t ttl() const { return _ttl; } +private: + uint16_t _projectInstallationId = 0; + uint8_t _ipAssignmentMethod = 0; + uint8_t _ipCapabilities = 0; + uint32_t _ipAddress = 0; + uint32_t _subnetMask = 0; + uint32_t _defaultGateway = 0; + uint32_t _multicastAddress = 0; + uint8_t _ttl = 60; + char _friendlyName[30] = { 0 }; + DeviceObject& _deviceObject; + Platform& _platform; + + void loadEvent(uint8_t* data); + void loadEventUnloaded(uint8_t* data); + void loadEventLoading(uint8_t* data); + void loadEventLoaded(uint8_t* data); + void loadEventError(uint8_t* data); + void additionalLoadControls(uint8_t* data); + void loadState(LoadState newState); + LoadState _state = LS_UNLOADED; + ErrorCode _errorCode = E_NO_FAULT; +}; \ No newline at end of file diff --git a/knx-esp.vcxitems b/knx-esp.vcxitems new file mode 100644 index 0000000..8228399 --- /dev/null +++ b/knx-esp.vcxitems @@ -0,0 +1,75 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + true + {83464ca3-e0d0-4486-82f4-f658f31ddc69} + + + + %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/knx-esp.vcxitems.filters b/knx-esp.vcxitems.filters new file mode 100644 index 0000000..82ef4d0 --- /dev/null +++ b/knx-esp.vcxitems.filters @@ -0,0 +1,171 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;s + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + + + Header Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + \ No newline at end of file diff --git a/knx_esp.h b/knx_esp.h new file mode 100644 index 0000000..d737518 --- /dev/null +++ b/knx_esp.h @@ -0,0 +1,19 @@ +/* + Name: knx_esp.h + Created: 01.03.2018 21:14:55 + Author: tkunze + Editor: http://www.visualmicro.com +*/ + +#ifndef _knx_esp_h +#define _knx_esp_h + +#if defined(ARDUINO) && ARDUINO >= 100 + #include "arduino.h" +#else + #include "WProgram.h" +#endif + + +#endif + diff --git a/knx_types.h b/knx_types.h new file mode 100644 index 0000000..1c3cf81 --- /dev/null +++ b/knx_types.h @@ -0,0 +1,103 @@ +#pragma once + +enum FrameFormat +{ + ExtendedFrame = 0, + StandardFrame = 0x80 +}; + +enum Priority +{ + LowPriority = 0xC, + NormalPriority = 0x4, + UrgentPriority = 0x8, + SystemPriority = 0x0 +}; + +enum AckType +{ + AckDontCare = 0, + AckRequested = 0x2, +}; + +enum AddressType +{ + InduvidualAddress = 0, + GroupAddress = 0x80, +}; + +enum MessageCode +{ + L_data_ind = 0x29, +}; + +enum Repetition +{ + NoRepitiion = 0, + WasRepeated = 0, + RepititionAllowed = 0x20, + WasNotRepeated = 0x20, +}; + +enum SystemBroadcast +{ + SysBroadcast = 0, + Broadcast = 0x10, +}; + +enum Confirm +{ + ConfirmNoError = 0, + ConfirmError = 1, +}; + +enum HopCountType +{ + UnlimitedRouting, + NetworkLayerParameter +}; + +enum TpduType +{ + DataBroadcast, + DataGroup, + DataInduvidual, + DataConnected, + Connect, + Disconnect, + Ack, + Nack, +}; + +enum ApduType +{ + GroupValueRead = 0x000, + GroupValueResponse = 0x040, + GroupValueWrite = 0x080, + IndividualAddressWrite = 0x0c0, + IndividualAddressRead = 0x100, + IndividualAddressResponse = 0x140, + MemoryRead = 0x200, + MemoryResponse = 0x240, + MemoryWrite = 0x280, + UserMemoryRead = 0x2C0, + UserMemoryResponse = 0x2C1, + UserMemoryWrite = 0x2C2, + UserManufacturerInfoRead = 0x2C5, + UserManufacturerInfoResponse = 0x2C6, + DeviceDescriptorRead = 0x300, + DeviceDescriptorResponse = 0x340, + Restart = 0x380, + AuthorizeRequest = 0x3d1, + AuthorizeResponse = 0x3d2, + KeyWrite = 0x3d3, + KeyResponse = 0x3d4, + PropertyValueRead = 0x3d5, + PropertyValueResponse = 0x3d6, + PropertyValueWrite = 0x3d7, + PropertyDescriptionRead = 0x3d8, + PropertyDescriptionResponse = 0x3d9, + IndividualAddressSerialNumberRead = 0x3dc, + IndividualAddressSerialNumberResponse = 0x3dd, + IndividualAddressSerialNumberWrite = 0x3de, +}; \ No newline at end of file diff --git a/library.properties b/library.properties new file mode 100644 index 0000000..0adb20f --- /dev/null +++ b/library.properties @@ -0,0 +1,9 @@ +name=knx_esp +version=1.0.0 +author=tkunze +maintainer=tkunze +sentence=knx_esp Library +paragraph= +category=Uncategorized +url=https://github/knx_esp +architectures=* \ No newline at end of file diff --git a/memory.cpp b/memory.cpp new file mode 100644 index 0000000..426b134 --- /dev/null +++ b/memory.cpp @@ -0,0 +1,47 @@ +#include "memory.h" + +Memory::Memory(Platform & platform): _platform(platform) +{ +} + +void Memory::memoryModified() +{ + _modified = true; +} + +bool Memory::isMemoryModified() +{ + return _modified; +} + +void Memory::readMemory() +{ + //_data = _platform.getEepromBuffer(512); + //uint8_t* buffer = _data; + //int size = _saveCount; + //for (int i = 0; i < size; i++) + //{ + // buffer = _saveRestores[i]->restore(buffer); + //} +} + +void Memory::writeMemory() +{ + //uint8_t* buffer = _data; + //int size = _saveCount; + //for (int i = 0; i < size; i++) + //{ + // buffer = _saveRestores[i]->save(buffer); + //} + //_platform.commitToEeprom(); + //_modified = false; +} + +void Memory::addSaveRestore(SaveRestore * obj) +{ + if (_saveCount >= MAXSAVE - 1) + return; + + _saveRestores[_saveCount] = obj; + _saveCount += 1; +} diff --git a/memory.h b/memory.h new file mode 100644 index 0000000..a633802 --- /dev/null +++ b/memory.h @@ -0,0 +1,24 @@ +#pragma once + +#include +#include "save_restore.h" +#include "platform.h" + +#define MAXSAVE 10 + +class Memory +{ +public: + Memory(Platform& platform); + void memoryModified(); + bool isMemoryModified(); + void readMemory(); + void writeMemory(); + void addSaveRestore(SaveRestore* obj); +private: + Platform& _platform; + bool _modified = false; + SaveRestore* _saveRestores[MAXSAVE]; + int _saveCount = 0; + uint8_t* _data = 0; +}; diff --git a/network_layer.cpp b/network_layer.cpp new file mode 100644 index 0000000..83bba34 --- /dev/null +++ b/network_layer.cpp @@ -0,0 +1,126 @@ +#include "network_layer.h" +#include "tpdu.h" +#include "cemi_frame.h" +#include "data_link_layer.h" + +NetworkLayer::NetworkLayer(TransportLayer& layer): _transportLayer(layer) +{ + +} + +void NetworkLayer::dataLinkLayer(DataLinkLayer& layer) +{ + _dataLinkLayer = &layer; +} + +uint8_t NetworkLayer::hopCount() const +{ + return _hopCount; +} + +void NetworkLayer::hopCount(uint8_t value) +{ + _hopCount = value & 0x7; +} + +void NetworkLayer::dataIndication(AckType ack, AddressType addrType, uint16_t destination, FrameFormat format, NPDU& npdu, Priority priority, uint16_t source) +{ + HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter; + if (addrType == InduvidualAddress) + { + //if (npdu.octetCount() > 0) + //{ + // print.print("<- NL "); + // npdu.frame().apdu().printPDU(); + //} + _transportLayer.dataIndividualIndication(destination, hopType, priority, source, npdu.tpdu()); + return; + } + // group-address type + if (destination != 0) + { + _transportLayer.dataGroupIndication(destination, hopType, priority, source, npdu.tpdu()); + return; + } + // destination == 0 + _transportLayer.dataBroadcastIndication(hopType, priority, source, npdu.tpdu()); + +} + +void NetworkLayer::dataConfirm(AckType ack, AddressType addressType, uint16_t destination, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status) +{ + HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter; + if (addressType == InduvidualAddress) + { + _transportLayer.dataIndividualConfirm(ack, destination, hopType, priority, npdu.tpdu(), status); + return; + } + // group-address type + if (destination != 0) + { + _transportLayer.dataGroupConfirm(ack, source, destination, hopType, priority, npdu.tpdu(), status); + return; + } + // destination == 0 + _transportLayer.dataBroadcastConfirm(ack, hopType, priority, npdu.tpdu(), status); +} + +void NetworkLayer::systemBroadcastIndication(AckType ack, FrameFormat format, NPDU& npdu, Priority priority, uint16_t source) +{ + HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter; + _transportLayer.dataBroadcastIndication(hopType, priority, source, npdu.tpdu()); +} + +void NetworkLayer::systemBroadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status) +{ + HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter; + _transportLayer.dataBroadcastConfirm(ack, hopType, priority, npdu.tpdu(), status); +} + +void NetworkLayer::dataIndividualRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) +{ + //if (tpdu.apdu().length() > 0) + //{ + // print.print("-> NL "); + // tpdu.apdu().printPDU(); + //} + sendDataRequest(tpdu, hopType, ack, destination, priority, InduvidualAddress); +} + +void NetworkLayer::sendDataRequest(TPDU &tpdu, HopCountType hopType, AckType ack, uint16_t destination, Priority priority, AddressType addrType) +{ + NPDU& npdu = tpdu.frame().npdu(); + + if (hopType == UnlimitedRouting) + npdu.hopCount(7); + else + npdu.hopCount(_hopCount); + + FrameFormat frameFormat = npdu.octetCount() > 15 ? ExtendedFrame : StandardFrame; + + _dataLinkLayer->dataRequest(ack, addrType, destination, frameFormat, priority, npdu); +} + +void NetworkLayer::dataGroupRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) +{ + sendDataRequest(tpdu, hopType, ack, destination, priority, GroupAddress); +} + +void NetworkLayer::dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) +{ + sendDataRequest(tpdu, hopType, ack, 0, priority, GroupAddress); +} + +void NetworkLayer::dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) +{ + NPDU& npdu = tpdu.frame().npdu(); + + if (hopType == UnlimitedRouting) + npdu.hopCount(7); + else + npdu.hopCount(_hopCount); + + FrameFormat frameFormat = npdu.octetCount() > 15 ? ExtendedFrame : StandardFrame; + + _dataLinkLayer->systemBroadcastRequest(ack, frameFormat, priority, npdu); +} diff --git a/network_layer.h b/network_layer.h new file mode 100644 index 0000000..cc94b31 --- /dev/null +++ b/network_layer.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include "knx_types.h" +#include "npdu.h" +#include "transport_layer.h" +class DataLinkLayer; + +class NetworkLayer +{ +public: + NetworkLayer(TransportLayer& layer); + + void dataLinkLayer(DataLinkLayer& layer); + uint8_t hopCount() const; + void hopCount(uint8_t value); + + // from data link layer + void dataIndication(AckType ack, AddressType addType, uint16_t destination, FrameFormat format, NPDU& npdu, + Priority priority, uint16_t source); + void dataConfirm(AckType ack, AddressType addressType, uint16_t destination, FrameFormat format, Priority priority, + uint16_t source, NPDU& npdu, bool status); + void systemBroadcastIndication(AckType ack, FrameFormat format, NPDU& npdu, + Priority priority, uint16_t source); + void systemBroadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status); + + // from transport layer + void dataIndividualRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu); + void dataGroupRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu); + void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu); + void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu); + +private: + void sendDataRequest(TPDU &tpdu, HopCountType hopType, AckType ack, uint16_t destination, Priority priority, AddressType addrType); + uint8_t _hopCount = 6; + DataLinkLayer* _dataLinkLayer; + TransportLayer& _transportLayer; +}; diff --git a/npdu.cpp b/npdu.cpp new file mode 100644 index 0000000..a5b840c --- /dev/null +++ b/npdu.cpp @@ -0,0 +1,44 @@ +#include "npdu.h" +#include "cemi_frame.h" +#include + + +NPDU::NPDU(uint8_t* data, CemiFrame& frame): _data(data), _frame(frame) +{ +} + + +uint8_t NPDU::octetCount() const +{ + return _data[0]; +} + +void NPDU::octetCount(uint8_t value) +{ + _data[0] = value; +} + +uint8_t NPDU::length() const +{ + return _data[0] + 2; // +1 for length field, +1 for TCPI +} + +uint8_t NPDU::hopCount() const +{ + return _frame.hopCount(); +} + +void NPDU::hopCount(uint8_t value) +{ + _frame.hopCount(value); +} + +CemiFrame& NPDU::frame() +{ + return _frame; +} + +TPDU& NPDU::tpdu() +{ + return _frame.tpdu(); +} diff --git a/npdu.h b/npdu.h new file mode 100644 index 0000000..3c4bf84 --- /dev/null +++ b/npdu.h @@ -0,0 +1,24 @@ +#pragma once + +#include + +class CemiFrame; +class TPDU; + +class NPDU +{ + friend class CemiFrame; +public: + NPDU(uint8_t* data, CemiFrame& frame); + + uint8_t octetCount() const; + void octetCount(uint8_t value); + uint8_t length() const; + uint8_t hopCount() const; + void hopCount(uint8_t value); + CemiFrame& frame(); + TPDU& tpdu(); +private: + uint8_t* _data; + CemiFrame& _frame; +}; \ No newline at end of file diff --git a/platform.h b/platform.h new file mode 100644 index 0000000..9efa224 --- /dev/null +++ b/platform.h @@ -0,0 +1,26 @@ +#pragma once + +#include +#include "save_restore.h" + +class Platform +{ +public: + virtual uint32_t currentIpAddress() = 0; + virtual uint32_t currentSubnetMask() = 0; + virtual uint32_t currentDefaultGateway() = 0; + virtual void macAddress(uint8_t* data) = 0; + + virtual uint32_t millis() = 0; + virtual void restart() = 0; + virtual void fatalError() = 0; + virtual void mdelay(uint32_t millis) = 0; + + virtual void setupMultiCast(uint32_t addr, uint16_t port) = 0; + virtual void closeMultiCast() = 0; + virtual bool sendBytes(uint8_t* buffer, uint16_t len) = 0; + virtual int readBytes(uint8_t* buffer, uint16_t maxLen) = 0; + + virtual uint8_t* getEepromBuffer(uint16_t size) = 0; + virtual void commitToEeprom() = 0; +}; \ No newline at end of file diff --git a/property_types.h b/property_types.h new file mode 100644 index 0000000..69c522b --- /dev/null +++ b/property_types.h @@ -0,0 +1,166 @@ +/* + * property_types.h - BCU 2 property types of EIB objects. + * + * Copyright (c) 2014 Stefan Taferner + * + * 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 + +enum PropertyDataType +{ + PDT_CONTROL = 0x00, //!< length: 1 read, 10 write + PDT_CHAR = 0x01, //!< length: 1 + PDT_UNSIGNED_CHAR = 0x02, //!< length: 1 + PDT_INT = 0x03, //!< length: 2 + PDT_UNSIGNED_INT = 0x04, //!< length: 2 + PDT_KNX_FLOAT = 0x05, //!< length: 2 + PDT_DATE = 0x06, //!< length: 3 + PDT_TIME = 0x07, //!< length: 3 + PDT_LONG = 0x08, //!< length: 4 + PDT_UNSIGNED_LONG = 0x09, //!< length: 4 + PDT_FLOAT = 0x0a, //!< length: 4 + PDT_DOUBLE = 0x0b, //!< length: 8 + PDT_CHAR_BLOCK = 0x0c, //!< length: 10 + PDT_POLL_GROUP_SETTING = 0x0d, //!< length: 3 + PDT_SHORT_CHAR_BLOCK = 0x0e, //!< length: 5 + PDT_DATE_TIME = 0x0f, //!< length: 8 + PDT_VARIABLE_LENGTH = 0x10, + PDT_GENERIC_01 = 0x11, //!< length: 1 + PDT_GENERIC_02 = 0x12, //!< length: 2 + PDT_GENERIC_03 = 0x13, //!< length: 3 + PDT_GENERIC_04 = 0x14, //!< length: 4 + PDT_GENERIC_05 = 0x15, //!< length: 5 + PDT_GENERIC_06 = 0x16, //!< length: 6 + PDT_GENERIC_07 = 0x17, //!< length: 7 + PDT_GENERIC_08 = 0x18, //!< length: 8 + PDT_GENERIC_09 = 0x19, //!< length: 9 + PDT_GENERIC_10 = 0x1a, //!< length: 10 + PDT_GENERIC_11 = 0x1b, //!< length: 11 + PDT_GENERIC_12 = 0x1c, //!< length: 12 + PDT_GENERIC_13 = 0x1d, //!< length: 13 + PDT_GENERIC_14 = 0x1e, //!< length: 14 + PDT_GENERIC_15 = 0x1f, //!< length: 15 + PDT_GENERIC_16 = 0x20, //!< length: 16 + PDT_GENERIC_17 = 0x21, //!< length: 17 + PDT_GENERIC_18 = 0x22, //!< length: 18 + PDT_GENERIC_19 = 0x23, //!< length: 19 + PDT_GENERIC_20 = 0x24, //!< length: 20 + PDT_UTF8 = 0x2f, //!< length: 3 + PDT_VERSION = 0x30, //!< length: 3 + PDT_ALARM_INFO = 0x31, //!< length: 3 + PDT_BINARY_INFORMATION = 0x32, //!< length: 3 + PDT_BITSET8 = 0x33, //!< length: 3 + PDT_BITSET16 = 0x34, //!< length: 3 + PDT_ENUM8 = 0x35, //!< length: 3 + PDT_SCALING = 0x36, //!< length: 3 + PDT_NE_VL = 0x3c, //!< length: 3 + PDT_NE_FL = 0x3d, //!< length: 3 + PDT_FUNCTION = 0x3e, //!< length: 3 + PDT_ESCAPE = 0x3f, //!< length: 3 +}; + +enum PropertyID +{ + /** Interface Object Type independent Properties */ + PID_OBJECT_TYPE = 1, + PID_LOAD_STATE_CONTROL = 5, + PID_RUN_STATE_CONTROL = 6, + PID_TABLE_REFERENCE = 7, + PID_SERVICE_CONTROL = 8, + PID_FIRMWARE_REVISION = 9, + PID_SERIAL_NUMBER = 11, + PID_MANUFACTURER_ID = 12, + PID_PROG_VERSION = 13, + PID_DEVICE_CONTROL = 14, + PID_ORDER_INFO = 15, + PID_PEI_TYPE = 16, + PID_PORT_CONFIGURATION = 17, + PID_VERSION = 25, + PID_MCB_TABLE = 27, + PID_ERROR_CODE = 28, + + /** Properties in the Device Object */ + PID_ROUTING_COUNT = 51, + PID_PROG_MODE = 54, + PID_MAX_APDU_LENGTH = 56, + PID_SUBNET_ADDR = 57, + PID_DEVICE_ADDR = 58, + PID_IO_LIST = 71, + PID_HARDWARE_TYPE = 78, + PID_DEVICE_DESCRIPTOR = 83, + + /** KNXnet/IP Parameter Object */ + PID_PROJECT_INSTALLATION_ID = 51, + PID_KNX_INDIVIDUAL_ADDRESS = 52, + PID_ADDITIONAL_INDIVIDUAL_ADDRESSES = 53, + PID_CURRENT_IP_ASSIGNMENT_METHOD = 54, + PID_IP_ASSIGNMENT_METHOD = 55, + PID_IP_CAPABILITIES = 56, + PID_CURRENT_IP_ADDRESS = 57, + PID_CURRENT_SUBNET_MASK = 58, + PID_CURRENT_DEFAULT_GATEWAY = 59, + PID_IP_ADDRESS = 60, + PID_SUBNET_MASK = 61, + PID_DEFAULT_GATEWAY = 62, + PID_DHCP_BOOTP_SERVER = 63, + PID_MAC_ADDRESS = 64, + PID_SYSTEM_SETUP_MULTICAST_ADDRESS = 65, + PID_ROUTING_MULTICAST_ADDRESS = 66, + PID_TTL = 67, + PID_KNXNETIP_DEVICE_CAPABILITIES = 68, + PID_KNXNETIP_DEVICE_STATE = 69, + PID_KNXNETIP_ROUTING_CAPABILITIES = 70, + PID_PRIORITY_FIFO_ENABLED = 71, + PID_QUEUE_OVERFLOW_TO_IP = 72, + PID_QUEUE_OVERFLOW_TO_KNX = 73, + PID_MSG_TRANSMIT_TO_IP = 74, + PID_MSG_TRANSMIT_TO_KNX = 75, + PID_FRIENDLY_NAME = 76, + PID_ROUTING_BUSY_WAIT_TIME = 78, +}; + +enum LoadState +{ + LS_UNLOADED = 0, + LS_LOADED = 1, + LS_LOADING = 2, + LS_ERROR = 3, + LS_UNLOADING = 4, + LS_LOADCOMPLETING = 5 +}; + +enum LoadEvents +{ + LE_NOOP = 0, + LE_START_LOADING = 1, + LE_LOAD_COMPLETED = 2, + LE_ADDITIONAL_LOAD_CONTROLS = 3, + LE_UNLOAD = 4 +}; + +// 20.011 DPT_ErrorClass_System +enum ErrorCode +{ + E_NO_FAULT = 0, + E_GENERAL_DEVICE_FAULT = 1, + E_COMMUNICATION_FAULT = 2, + E_CONFIGURATION_FAULT = 3, + E_HARDWARE_FAULT = 4, + E_SOFTWARE_FAULT = 5, + E_INSUFFICIENT_NON_VOLATILE_MEMORY = 6, + E_INSUFFICIENT_VOLATILE_MEMORY = 7, + E_GOT_MEM_ALLOC_ZERO = 8, + E_CRC_ERROR = 9, + E_WATCHDOG_RESET = 10, + E_INVALID_OPCODE = 11, + E_GENERAL_PROTECTION_FAULT = 12, + E_MAX_TABLE_LENGTH_EXEEDED = 13, + E_GOT_UNDEF_LOAD_CMD = 14, + E_GAT_NOT_SORTED = 15, + E_INVALID_CONNECTION_NUMBER = 16, + E_INVALID_GO_NUMBER = 17, + E_GO_TYPE_TOO_BIG = 18 +}; \ No newline at end of file diff --git a/readme.txt b/readme.txt new file mode 100644 index 0000000..ac2d41e --- /dev/null +++ b/readme.txt @@ -0,0 +1,19 @@ +Arduino Compatible Cross Platform C++ Library Project : For more information see http://www.visualmicro.com + +This project works exactly the same way as an Arduino library. + +Add this project to any solution that contains an Arduino project and #include in code as you would any normal Arduino library headers. + +To enable intellisense and to support live build discovery outside of the "standard" Arduino library locations, ensure that the library is added as a shared project reference to the master Arduino project. To do this, right click the master project "References" node and then click "Add Reference". A window will open and the library will appear on the "Shared Projects" tab. Click the checkbox next to the library name to add the reference. If this library is moved the shared referencemust be removed and re-added. + +VS2017 has a bug, workround: After moving existing source code within a "library or shared project", close and re-open the solution. + +Visual Studio will display intellisense for libraries based on the platform/board that has been specified for the currently active "Startup Project" of the current solution. + + +IMPORTANT: The arduino.cc Library Rules must be followed when adding code or restructing libraries. + + + + +blog: http://www.visualmicro.com/post/2017/01/16/Arduino-Cross-Platform-Library-Development.aspx \ No newline at end of file diff --git a/save_restore.h b/save_restore.h new file mode 100644 index 0000000..8a003db --- /dev/null +++ b/save_restore.h @@ -0,0 +1,10 @@ +#pragma once +#include + + +class SaveRestore +{ +public: + virtual uint8_t* save(uint8_t* buffer) = 0; + virtual uint8_t* restore(uint8_t* buffer) = 0; +}; \ No newline at end of file diff --git a/table_object.cpp b/table_object.cpp new file mode 100644 index 0000000..e5ed445 --- /dev/null +++ b/table_object.cpp @@ -0,0 +1,257 @@ +#include +#include + +#include "table_object.h" +#include "bits.h" + +TableObject::TableObject(uint8_t* memoryReference): _memoryReference(memoryReference) +{ + +} + +void TableObject::readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data) +{ + switch (id) + { + case PID_LOAD_STATE_CONTROL: + data[0] = _state; + break; + case PID_TABLE_REFERENCE: + if (_state == LS_UNLOADED) + pushInt(0, data); + else + pushInt(tableReference(), data); + break; + case PID_ERROR_CODE: + data[0] = _errorCode; + break; + } +} + +void TableObject::writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count) +{ + switch (id) + { + case PID_LOAD_STATE_CONTROL: + loadEvent(data); + break; + + //case PID_MCB_TABLE: + // TODO + // break; + } +} + +uint8_t TableObject::propertySize(PropertyID id) +{ + switch (id) + { + case PID_LOAD_STATE_CONTROL: + return 10; + + //case PID_MCB_TABLE: + // TODO + // break; + } + return 0; +} + +TableObject::~TableObject() +{ + if (_data != 0) + free(_data); +} + +LoadState TableObject::loadState() +{ + return _state; +} + +void TableObject::loadState(LoadState newState) +{ + if (newState == _state) + return; + beforeStateChange(newState); + _state = newState; +} + + +uint8_t* TableObject::save(uint8_t* buffer) +{ + buffer = pushByte(_state, buffer); + buffer = pushByte(_errorCode, buffer); + buffer = pushInt(_size, buffer); + buffer = pushByteArray(_data, _size, buffer); + + return buffer; +} + + +uint8_t* TableObject::restore(uint8_t* buffer) +{ + uint8_t state = 0; + uint8_t errorCode = 0; + buffer = popByte(state, buffer); + buffer = popByte(errorCode, buffer); + _state = (LoadState)state; + _errorCode = (ErrorCode)errorCode; + + buffer = popInt(_size, buffer); + + if (_data) + free(_data); + + _data = (uint8_t*) malloc(_size); + + buffer = popByteArray(_data, _size, buffer); + + return buffer; +} + +uint32_t TableObject::tableReference() +{ + return (uint32_t)(_data - _memoryReference); +} + +bool TableObject::allocTable(uint32_t size, bool doFill, uint8_t fillByte) +{ + if (_data) + { + free(_data); + _size = 0; + } + + _data = (uint8_t*)malloc(size); + if (!_data) + return false; + + _size = size; + + if (doFill) + memset(_data, fillByte, size); + + return true; +} + +void TableObject::loadEvent(uint8_t* data) +{ + switch (_state) + { + case LS_UNLOADED: + loadEventUnloaded(data); + break; + case LS_LOADING: + loadEventLoading(data); + break; + case LS_LOADED: + loadEventLoaded(data); + break; + case LS_ERROR: + loadEventError(data); + break; + } +} + +void TableObject::loadEventUnloaded(uint8_t* data) +{ + uint8_t event = data[0]; + switch (event) + { + case LE_NOOP: + case LE_LOAD_COMPLETED: + case LE_ADDITIONAL_LOAD_CONTROLS: + case LE_UNLOAD: + break; + case LE_START_LOADING: + loadState(LS_LOADING); + break; + default: + loadState(LS_ERROR); + _errorCode = E_GOT_UNDEF_LOAD_CMD; + } +} + +void TableObject::loadEventLoading(uint8_t* data) +{ + uint8_t event = data[0]; + switch (event) + { + case LE_NOOP: + case LE_START_LOADING: + break; + case LE_LOAD_COMPLETED: + loadState(LS_LOADED); + break; + case LE_UNLOAD: + loadState(LS_UNLOADED); + break; + case LE_ADDITIONAL_LOAD_CONTROLS: + additionalLoadControls(data); + break; + default: + loadState(LS_ERROR); + _errorCode = E_GOT_UNDEF_LOAD_CMD; + } +} + +void TableObject::loadEventLoaded(uint8_t* data) +{ + uint8_t event = data[0]; + switch (event) + { + case LE_NOOP: + case LE_LOAD_COMPLETED: + break; + case LE_START_LOADING: + loadState(LS_LOADING); + break; + case LE_UNLOAD: + loadState(LS_UNLOADED); + break; + case LE_ADDITIONAL_LOAD_CONTROLS: + loadState(LS_ERROR); + _errorCode = E_INVALID_OPCODE; + break; + default: + loadState(LS_ERROR); + _errorCode = E_GOT_UNDEF_LOAD_CMD; + } +} + +void TableObject::loadEventError(uint8_t* data) +{ + uint8_t event = data[0]; + switch (event) + { + case LE_NOOP: + case LE_LOAD_COMPLETED: + case LE_ADDITIONAL_LOAD_CONTROLS: + case LE_START_LOADING: + break; + case LE_UNLOAD: + loadState(LS_UNLOADED); + break; + default: + loadState(LS_ERROR); + _errorCode = E_GOT_UNDEF_LOAD_CMD; + } +} + +void TableObject::additionalLoadControls(uint8_t* data) +{ + if (data[1] != 0x0B) // Data Relative Allocation + { + loadState(LS_ERROR); + _errorCode = E_INVALID_OPCODE; + return; + } + + size_t size = ((data[2] << 24) | (data[3] << 16) | (data[4] << 8) | data[5]); + bool doFill = data[6] == 0x1; + uint8_t fillByte = data[7]; + if (!allocTable(size, doFill, fillByte)) + { + loadState(LS_ERROR); + _errorCode = E_MAX_TABLE_LENGTH_EXEEDED; + } +} \ No newline at end of file diff --git a/table_object.h b/table_object.h new file mode 100644 index 0000000..b5f23af --- /dev/null +++ b/table_object.h @@ -0,0 +1,33 @@ +#pragma once + +#include "interface_object.h" + +class TableObject: public InterfaceObject +{ +public: + TableObject(uint8_t* memoryReference); + virtual void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data); + virtual void writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count); + virtual uint8_t propertySize(PropertyID id); + virtual ~TableObject(); + LoadState loadState(); + virtual uint8_t* save(uint8_t* buffer); + virtual uint8_t* restore(uint8_t* buffer); +protected: + virtual void beforeStateChange(LoadState& newState) {} + uint8_t* _data = 0; + uint32_t _size = 0; + ErrorCode _errorCode = E_NO_FAULT; +private: + uint32_t tableReference(); + bool allocTable(uint32_t size, bool doFill, uint8_t fillByte); + void loadEvent(uint8_t* data); + void loadEventUnloaded(uint8_t* data); + void loadEventLoading(uint8_t* data); + void loadEventLoaded(uint8_t* data); + void loadEventError(uint8_t* data); + void additionalLoadControls(uint8_t* data); + void loadState(LoadState newState); + LoadState _state = LS_UNLOADED; + uint8_t* _memoryReference; +}; diff --git a/tpdu.cpp b/tpdu.cpp new file mode 100644 index 0000000..ff4c6ee --- /dev/null +++ b/tpdu.cpp @@ -0,0 +1,127 @@ +#include "tpdu.h" +#include "cemi_frame.h" + +TPDU::TPDU(uint8_t* data, CemiFrame& frame): _data(data), _frame(frame) +{ +} + +TpduType TPDU::type() const +{ + if (control()) + { + if (numbered()) + { + if ((_data[0] & 1) == 0) + return Ack; + else + return Nack; + } + else if ((_data[0] & 1) == 0) + return Connect; + else + return Disconnect; + } + else + { + if (_frame.addressType() == GroupAddress) + { + if (_frame.destinationAddress() == 0) + return DataBroadcast; + else + return DataGroup; + } + else if (numbered()) + return DataConnected; + else + return DataInduvidual; + } +} + +void TPDU::type(TpduType type) +{ + switch (type) + { + case DataBroadcast: + case DataGroup: + case DataInduvidual: + _data[0] &= 0x3; + break; + case DataConnected: + _data[0] &= 0xC3; + _data[0] |= 0x40; + break; + case Connect: + _data[0] = 0x80; + break; + case Disconnect: + _data[0] = 0x81; + break; + case Ack: + _data[0] &= ~0xC3; + _data[0] |= 0xC2; + break; + case Nack: + _data[0] |= 0xC3; + break; + } +} + +bool TPDU::numbered() const +{ + return (_data[0] & 0x40) > 0; +} + +void TPDU::numbered(bool value) +{ + if (value) + _data[0] |= 0x40; + else + _data[0] &= ~0x40; +} + +bool TPDU::control() const +{ + return (_data[0] & 0x80) > 0; +} + +void TPDU::control(bool value) +{ + if (value) + _data[0] |= 0x80; + else + _data[0] &= ~0x80; +} + +uint8_t TPDU::sequenceNumber() const +{ + return ((_data[0] >> 2) & 0xF); +} + +void TPDU::sequenceNumber(uint8_t value) +{ + _data[0] &= ~(0xF << 2); + _data[0] |= ((value & 0xF) << 2); +} + +APDU& TPDU::apdu() +{ + return _frame.apdu(); +} + +CemiFrame& TPDU::frame() +{ + return _frame; +} + +void TPDU::printPDU() +{ +/* print.print("TPDU: "); + print.print(type(), HEX, 2); + print.print(" "); + for (uint8_t i = 0; i < apdu().length() + 1; ++i) + { + if (i) print.print(" "); + print.print(_data[i], HEX, 2); + } + print.println()*/; +} \ No newline at end of file diff --git a/tpdu.h b/tpdu.h new file mode 100644 index 0000000..b981443 --- /dev/null +++ b/tpdu.h @@ -0,0 +1,32 @@ +#pragma once + +#include "stdint.h" +#include "knx_types.h" +class CemiFrame; +class APDU; + +class TPDU +{ + friend class CemiFrame; +public: + TPDU(uint8_t* data, CemiFrame& frame); + TpduType type() const; + void type(TpduType type); + + bool numbered() const; + void numbered(bool value); + + bool control() const; + void control(bool value); + + uint8_t sequenceNumber() const; + void sequenceNumber(uint8_t value); + + APDU& apdu(); + + CemiFrame& frame(); + void printPDU(); +private: + uint8_t* _data; + CemiFrame& _frame; +}; diff --git a/transport_layer.cpp b/transport_layer.cpp new file mode 100644 index 0000000..92de287 --- /dev/null +++ b/transport_layer.cpp @@ -0,0 +1,711 @@ +#include "transport_layer.h" +#include "apdu.h" +#include "cemi_frame.h" +#include "network_layer.h" +#include "application_layer.h" +#include "platform.h" +#include + +TransportLayer::TransportLayer(ApplicationLayer& layer, AddressTableObject& gat, Platform& platform): _savedFrame(0), + _savedFrameConnecting(0), _applicationLayer(layer), _groupAddressTable(gat), _platform(platform) +{ + _currentState = Closed; +} + +void TransportLayer::networkLayer(NetworkLayer& layer) +{ + _networkLayer = &layer; +} + +void TransportLayer::dataIndividualIndication(uint16_t destination, HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu) +{ + //if (tpdu.apdu().length() > 0) + //{ + // print.print("<- TL "); + // tpdu.printPDU(); + // print.print("<- TL "); + // tpdu.apdu().printPDU(); + //} + + uint8_t sequenceNo = tpdu.sequenceNumber(); + switch (tpdu.type()) + { + case DataInduvidual: + _applicationLayer.dataIndividualIndication(hopType, priority, source, tpdu.apdu()); + return; + case DataConnected: + if (source == _connectionAddress) + { + if (sequenceNo == _seqNoRecv) + { + //E4 + switch (_currentState) + { + case Closed: + //A0 nothing + break; + case OpenIdle: + case OpenWait: + A2(source, priority, tpdu.apdu()); + break; + case Connecting: + _currentState = Closed; + A6(destination); + break; + } + } + else if(sequenceNo == ((_seqNoRecv -1) & 0xF)) + { + //E5 + switch (_currentState) + { + case Closed: + //A0 + break; + case OpenIdle: + case OpenWait: + case Connecting: + A3(source, priority, tpdu); + break; + } + } + else + { + //E6 + switch (_currentState) + { + case Closed: + //A0 + break; + case OpenIdle: + case OpenWait: + A4(source, priority, tpdu); + break; + case Connecting: + A6(destination); + break; + } + } + } + else + { + //E7 + switch (_currentState) + { + case Closed: + case OpenIdle: + case OpenWait: + //A0 + break; + case Connecting: + A10(source); + break; + } + } + break; + case Connect: + if (source == _connectionAddress) + { + //E0 + switch (_currentState) + { + case Closed: + _currentState = OpenIdle; + A1(source); + break; + case OpenWait: + case OpenIdle: + case Connecting: + //A0: do nothing + break; + } + } + else + { + //E1 + switch (_currentState) + { + case Closed: + _currentState = OpenIdle; + A1(source); + break; + case OpenIdle: + case OpenWait: + case Connecting: + A10(source); + break; + } + } + break; + case Disconnect: + if (source == _connectionAddress) + { + //E2 + switch (_currentState) + { + case Closed: + //A0 do nothing + break; + case OpenIdle: + case OpenWait: + case Connecting: + _currentState = Closed; + A5(source); + break; + default: + break; + } + } + else + { + //E3 + //A0: do nothing + } + break; + case Ack: + if (source == _connectionAddress) + { + if (sequenceNo == _seqNoSend) + { + //E8 + switch (_currentState) + { + case Closed: + case OpenIdle: + //A0 + break; + case OpenWait: + _currentState = OpenIdle; + A8(); + break; + case Connecting: + _currentState = Closed; + A6(source); + break; + } + } + else + { + //E9 + switch (_currentState) + { + case Closed: + case OpenIdle: + //A0 + break; + case OpenWait: + case Connecting: + _currentState = Closed; + A6(source); + break; + } + } + } + else + { + //E10 + switch (_currentState) + { + case Connecting: + A10(source); + break; + } + } + break; + case Nack: + if (source == _connectionAddress) + { + if (sequenceNo != _seqNoSend) + { + //E11 + switch (_currentState) + { + case Closed: + case OpenIdle: + case OpenWait: + //A0 + break; + case Connecting: + _currentState = Closed; + A6(source); + break; + } + } + else + { + if (_repCount < _maxRepCount) + { + //E12 + switch (_currentState) + { + case Closed: + //A0 + break; + case Connecting: + case OpenIdle: + _currentState = Closed; + A6(source); + break; + case OpenWait: + A9(); + break; + } + } + else + { + //E13 + switch (_currentState) + { + case Closed: + //A0 + break; + case OpenIdle: + case OpenWait: + case Connecting: + _currentState = Closed; + A6(source); + break; + } + } + } + } + else + { + //E14 + switch (_currentState) + { + case Closed: + case OpenIdle: + case OpenWait: + //A0 + break; + case Connecting: + A10(source); + break; + default: + break; + } + } + break; + default: + break; + } +} + +void TransportLayer::dataIndividualConfirm(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu, bool status) +{ + TpduType type = tpdu.type(); + switch (type) + { + case DataInduvidual: + _applicationLayer.dataIndividualConfirm(ack, hopType, priority, destination, tpdu.apdu(), status); + break; + case DataConnected: + //E22 + //A0: do nothing + break; + case Connect: + if (status) + { + //E19 + switch (_currentState) + { + case Closed: + case OpenIdle: + case OpenWait: + //A0: do nothing + break; + case Connecting: + _currentState = OpenIdle; + A13(destination); + break; + } + } + else + { + //E20 + switch (_currentState) + { + case Closed: + case OpenIdle: + case OpenWait: + //A0: do nothing + break; + case Connecting: + A5(destination); + break; + } + + } + break; + case Disconnect: + //E21 + //A0: do nothing + break; + case Ack: + //E23 + //A0: do nothing + break; + case Nack: + //E24 + //A0: do nothing + break; + } +} + +void TransportLayer::dataGroupIndication(uint16_t destination, HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu) +{ + uint16_t tsap = _groupAddressTable.getTsap(destination); + _applicationLayer.dataGroupIndication(hopType, priority, tsap, tpdu.apdu()); +} + +void TransportLayer::dataGroupConfirm(AckType ack, uint16_t source, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu, bool status) +{ + _applicationLayer.dataGroupConfirm(ack, hopType, priority, destination, tpdu.apdu(), status); +} + +void TransportLayer::dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu) +{ + _applicationLayer.dataBroadcastIndication(hopType, priority, source, tpdu.apdu()); +} + +void TransportLayer::dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu, bool status) +{ + _applicationLayer.dataBroadcastConfirm(ack, hopType, priority, tpdu.apdu(), status); +} + +void TransportLayer::dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu) +{ + _applicationLayer.dataSystemBroadcastIndication(hopType, priority, source, tpdu.apdu()); +} + +void TransportLayer::dataSystemBroadcastConfirm(AckType ack, HopCountType hopType, TPDU& tpdu, Priority priority, bool status) +{ + _applicationLayer.dataSystemBroadcastConfirm(hopType, priority, tpdu.apdu(), status); +} + +void TransportLayer::dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) +{ + uint16_t groupAdress = _groupAddressTable.getGa(tsap); + TPDU& tpdu = apdu.frame().tpdu(); + _networkLayer->dataGroupRequest(ack, groupAdress, hopType, priority, tpdu); +} + +void TransportLayer::dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu) +{ + TPDU& tpdu = apdu.frame().tpdu(); + _networkLayer->dataBroadcastRequest(ack, hopType, priority, tpdu); +} + +void TransportLayer::dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu) +{ + TPDU& tpdu = apdu.frame().tpdu(); + return _networkLayer->dataSystemBroadcastRequest(ack, hopType, priority, tpdu); +} + +void TransportLayer::dataIndividualRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t destination, APDU& apdu) +{ + //print.print("-> TL "); + //apdu.printPDU(); + TPDU& tpdu = apdu.frame().tpdu(); + _networkLayer->dataIndividualRequest(ack, destination, hopType, priority, tpdu); +} + +void TransportLayer::connectRequest(uint16_t destination, Priority priority) +{ + //E25 + switch (_currentState) + { + case Closed: + _currentState = Connecting; + A12(destination, priority); + break; + case OpenIdle: + case OpenWait: + case Connecting: + _currentState = Closed; + A6(destination); + break; + } +} + +void TransportLayer::disconnectRequest(uint16_t tsap, Priority priority) +{ + //E26 + switch (_currentState) + { + case Closed: + A15(priority, tsap); + break; + case OpenIdle: + case OpenWait: + case Connecting: + _currentState = Closed; + A14(tsap, priority); + break; + } +} + +void TransportLayer::dataConnectedRequest(uint16_t tsap, Priority priority, APDU& apdu) +{ + //print.print("-> TL "); + //apdu.printPDU(); + //E15 + switch (_currentState) + { + case Closed: + //A0 + break; + case OpenIdle: + _currentState = OpenWait; + A7(priority, apdu); + break; + case OpenWait: + case Connecting: + A11(tsap, priority, apdu); + break; + default: + break; + } +} + +void TransportLayer::connectionTimeoutIndication() +{ + //E16 + switch (_currentState) + { + case Closed: + //A0: do nothing + break; + case OpenIdle: + case OpenWait: + case Connecting: + _currentState = Closed; + A6(_connectionAddress); + break; + } +} + +void TransportLayer::ackTimeoutIndication() +{ + if (_repCount < _maxRepCount) + { + //E17 + switch (_currentState) + { + case Closed: + case OpenIdle: + case Connecting: + //A0: do nothing + break; + case OpenWait: + A9(); + break; + } + } + else + { + //E18 + switch (_currentState) + { + case Closed: + case OpenIdle: + case Connecting: + //A0: do nothing + break; + case OpenWait: + _currentState = Closed; + A6(_connectionAddress); + break; + } + } +} + + + +void TransportLayer::loop() +{ + uint32_t millis = _platform.millis(); + if (_connectionTimeoutEnabled + && (millis - _connectionTimeoutStartMillis) > _connectionTimeoutMillis) + connectionTimeoutIndication(); + + if (_ackTimeoutEnabled + && (millis - _ackTimeoutStartMillis) > _ackTimeoutMillis) + ackTimeoutIndication(); + + if (_savedConnectingValid) + { + //retry saved event + _savedConnectingValid = false; + dataConnectedRequest(_savedTsapConnecting, _savedPriorityConnecting, _savedFrameConnecting.apdu()); + } +} + +void TransportLayer::sendControlTelegram(TpduType pduType, uint8_t seqNo) +{ + CemiFrame frame(0); + TPDU& tpdu = frame.tpdu(); + tpdu.type(pduType); + tpdu.sequenceNumber(seqNo); + _networkLayer->dataIndividualRequest(AckRequested, _connectionAddress, NetworkLayerParameter, + SystemPriority, tpdu); +} + +void TransportLayer::A0() +{ + /* do nothing */ +} + +void TransportLayer::A1(uint16_t source) +{ + _connectionAddress = source; + _applicationLayer.connectIndication(source); + _seqNoSend = 0; + _seqNoRecv = 0; +} + +void incSeqNr(uint8_t& seqNr) +{ + seqNr += 1; + if (seqNr > 0xf) + seqNr = 0; +} + +void TransportLayer::A2(uint16_t source, Priority priority, APDU& apdu) +{ + sendControlTelegram(Ack, _seqNoRecv); + incSeqNr(_seqNoRecv); + _applicationLayer.dataConnectedIndication(priority, source, apdu); + enableConnectionTimeout(); +} + +void TransportLayer::A3(uint16_t source, Priority priority, TPDU& recTpdu) +{ + sendControlTelegram(Ack, recTpdu.sequenceNumber()); + enableConnectionTimeout(); +} + +void TransportLayer::A4(uint16_t source, Priority priority, TPDU& recTpdu) +{ + sendControlTelegram(Nack, recTpdu.sequenceNumber()); + enableConnectionTimeout(); +} + +void TransportLayer::A5(uint16_t tsap) +{ + _applicationLayer.disconnectIndication(tsap); + disableConnectionTimeout(); + disableAckTimeout(); +} + +void TransportLayer::A6(uint16_t tsap) +{ + sendControlTelegram(Disconnect, 0); + _applicationLayer.disconnectIndication(tsap); + disableConnectionTimeout(); + disableAckTimeout(); +} + +void TransportLayer::A7(Priority priority, APDU& apdu) +{ + _savedPriority = priority; + _savedFrame = apdu.frame(); + TPDU& tpdu = apdu.frame().tpdu(); + tpdu.type(DataConnected); + tpdu.sequenceNumber(_seqNoSend); + _networkLayer->dataIndividualRequest(AckRequested, _connectionAddress, NetworkLayerParameter, priority, tpdu); + _repCount = 0; + enableAckTimeout(); + enableConnectionTimeout(); +} + +void TransportLayer::A8() +{ + disableAckTimeout(); + incSeqNr(_seqNoSend); + _applicationLayer.dataConnectedConfirm(0); + enableConnectionTimeout(); +} + +void TransportLayer::A9() +{ + TPDU& tpdu = _savedFrame.tpdu(); + // tpdu is still initialized from last send + _networkLayer->dataIndividualRequest(AckRequested, _connectionAddress, NetworkLayerParameter, _savedPriority, tpdu); + _repCount += 1; + enableAckTimeout(); + enableConnectionTimeout(); +} + +void TransportLayer::A10(uint16_t source) +{ + CemiFrame frame(0); + TPDU& tpdu = frame.tpdu(); + tpdu.type(Disconnect); + tpdu.sequenceNumber(0); + _networkLayer->dataIndividualRequest(AckRequested, source, NetworkLayerParameter, SystemPriority, tpdu); +} + +void TransportLayer::A11(uint16_t tsap, Priority priority, APDU& apdu) +{ + _savedTsapConnecting = tsap; + _savedPriorityConnecting = priority; + _savedFrameConnecting = apdu.frame(); + _savedConnectingValid = true; +} + +void TransportLayer::A12(uint16_t destination, Priority priority) +{ + _connectionAddress = destination; + CemiFrame frame(0); + TPDU& tpdu = frame.tpdu(); + tpdu.type(Connect); + _seqNoRecv = 0; + _seqNoSend = 0; + enableConnectionTimeout(); +} + +void TransportLayer::A13(uint16_t destination) +{ + _applicationLayer.connectConfirm(destination, 0, true); +} + +void TransportLayer::A14(uint16_t tsap, Priority priority) +{ + CemiFrame frame(0); + TPDU& tpdu = frame.tpdu(); + tpdu.type(Disconnect); + tpdu.sequenceNumber(0); + _networkLayer->dataIndividualRequest(AckRequested, _connectionAddress, NetworkLayerParameter, SystemPriority, tpdu); + _applicationLayer.disconnectConfirm(priority, tsap, true); +} + +void TransportLayer::A15(Priority priority, uint16_t tsap) +{ + _applicationLayer.disconnectConfirm(priority, tsap, true); + disableConnectionTimeout(); + disableAckTimeout(); +} + +void TransportLayer::enableConnectionTimeout() +{ + _connectionTimeoutStartMillis = _platform.millis(); + _connectionTimeoutEnabled = true; +} + +void TransportLayer::disableConnectionTimeout() +{ + _connectionTimeoutEnabled = false; +} + +void TransportLayer::enableAckTimeout() +{ + _ackTimeoutStartMillis = _platform.millis(); + _ackTimeoutEnabled = true; +} + +void TransportLayer::disableAckTimeout() +{ + _ackTimeoutEnabled = false; +} diff --git a/transport_layer.h b/transport_layer.h new file mode 100644 index 0000000..5d883f4 --- /dev/null +++ b/transport_layer.h @@ -0,0 +1,98 @@ +#pragma once + +#include +#include "knx_types.h" +#include "tpdu.h" +#include "address_table_object.h" +#include "cemi_frame.h" + +class ApplicationLayer; +class APDU; +class NetworkLayer; +class Platform; + +enum StateType { Closed, OpenIdle, OpenWait, Connecting }; + +class TransportLayer +{ +public: + TransportLayer(ApplicationLayer& layer, AddressTableObject& gat, Platform& platform); + void networkLayer(NetworkLayer& layer); + + // from network layer + void dataIndividualIndication(uint16_t destination, HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu); + void dataIndividualConfirm(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu, bool status); + void dataGroupIndication(uint16_t destination, HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu); + void dataGroupConfirm(AckType ack, uint16_t source, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu, bool status); + void dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu); + void dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu, bool status); + void dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu); + void dataSystemBroadcastConfirm(AckType ack, HopCountType hopType, TPDU& tpdu, Priority priority, bool status); + + // fromp application layer + void dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu); + void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu); + void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu); + void dataIndividualRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t destination, APDU& apdu); + + void connectRequest(uint16_t destination, Priority priority); + void disconnectRequest(uint16_t tsap, Priority priority); + // apdu must be valid until it was confirmed + void dataConnectedRequest(uint16_t tsap, Priority priority, APDU& apdu); + + // other + void connectionTimeoutIndication(); + void ackTimeoutIndication(); + void loop(); +private: +#pragma region States + Priority _savedPriority = LowPriority; + CemiFrame _savedFrame; + Priority _savedPriorityConnecting; + CemiFrame _savedFrameConnecting; + uint16_t _savedTsapConnecting; + bool _savedConnectingValid = false; + enum StateEvent + { + E0, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, + E15, E16, E17, E18, E19, E20, E21, E22, E23, E24, E25, E26, E27 + }; + StateType _currentState = Closed; + void sendControlTelegram(TpduType pduType, uint8_t seqNo); + void A0(); + void A1(uint16_t source); + void A2(uint16_t source, Priority priority, APDU& apdu); + void A3(uint16_t source, Priority priority, TPDU& recTpdu); + void A4(uint16_t source, Priority priority, TPDU& recTpdu); + void A5(uint16_t source); + void A6(uint16_t source); + void A7(Priority priority, APDU& apdu); + void A8(); + void A9(); + void A10(uint16_t source); + void A11(uint16_t tsap, Priority priority, APDU& apdu); + void A12(uint16_t destination, Priority priority); + void A13(uint16_t destination); + void A14(uint16_t destination, Priority priority); + void A15(Priority priority, uint16_t tsap); + void enableConnectionTimeout(); + void disableConnectionTimeout(); + void enableAckTimeout(); + void disableAckTimeout(); + uint16_t _connectionAddress = 0; + uint8_t _seqNoSend = 0; + uint8_t _seqNoRecv = 0; + bool _connectionTimeoutEnabled = false; + uint32_t _connectionTimeoutStartMillis = 0; + uint16_t _connectionTimeoutMillis = 6000; + bool _ackTimeoutEnabled = false; + uint32_t _ackTimeoutStartMillis = 0; + uint16_t _ackTimeoutMillis = 3000; + uint8_t _repCount = 0; + uint8_t _maxRepCount = 3; +#pragma endregion + ApplicationLayer& _applicationLayer; + AddressTableObject& _groupAddressTable; + NetworkLayer* _networkLayer; + Platform& _platform; +}; From 649d831f7f5ceac243de63e6ab71ac1d129061de Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Sat, 17 Mar 2018 00:08:14 +0100 Subject: [PATCH 02/24] -add magic number to memory -add debug output to esp_platform -add more fine granular startup to Bau57B0 --- application_layer.cpp | 2 +- bau57B0.cpp | 18 ++++++++++++++++-- bau57B0.h | 3 +++ esp_platform.cpp | 32 ++++++++++++++++++++++++++------ memory.cpp | 39 ++++++++++++++++++++++++--------------- 5 files changed, 70 insertions(+), 24 deletions(-) diff --git a/application_layer.cpp b/application_layer.cpp index 62233f0..3cc3c8e 100644 --- a/application_layer.cpp +++ b/application_layer.cpp @@ -39,7 +39,7 @@ void ApplicationLayer::dataGroupIndication(HopCountType hopType, Priority priori len -= 1; } - for (uint16_t i; i < entries; i++) + for (uint16_t i = 0; i < entries; i++) { uint16_t entry = _assocTable[i]; if (highByte(entry) == tsap) diff --git a/bau57B0.cpp b/bau57B0.cpp index 85c1d4a..d78e3b1 100644 --- a/bau57B0.cpp +++ b/bau57B0.cpp @@ -2,6 +2,7 @@ #include #include + using namespace std; Bau57B0::Bau57B0(Platform& platform): _memoryReference((uint8_t*)&_deviceObj), _memory(platform), _addrTable(_memoryReference), @@ -19,8 +20,6 @@ Bau57B0::Bau57B0(Platform& platform): _memoryReference((uint8_t*)&_deviceObj), _ _memory.addSaveRestore(&_addrTable); _memory.addSaveRestore(&_assocTable); _memory.addSaveRestore(&_groupObjTable); - _memory.readMemory(); - _dlLayer.enabled(true); } void Bau57B0::loop() @@ -82,6 +81,11 @@ void Bau57B0::updateGroupObject(GroupObject & go, uint8_t * data, uint8_t length go.commFlag(cfUpdate); if (go.updateHandler) go.updateHandler(go); +} + +void Bau57B0::readMemory() +{ + _memory.readMemory(); } DeviceObject& Bau57B0::deviceObject() @@ -105,6 +109,16 @@ bool Bau57B0::configured() && _addrTable.loadState() == LS_LOADED && _assocTable.loadState() == LS_LOADED && _appProgram.loadState() == LS_LOADED; +} + +bool Bau57B0::enabled() +{ + return _dlLayer.enabled(); +} + +void Bau57B0::enabled(bool value) +{ + _dlLayer.enabled(value); } void Bau57B0::memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, diff --git a/bau57B0.h b/bau57B0.h index 1885e5d..5fd6718 100644 --- a/bau57B0.h +++ b/bau57B0.h @@ -24,6 +24,9 @@ public: GroupObjectTableObject& groupObjectTable(); ApplicationProgramObject& parameters(); bool configured(); + bool enabled(); + void enabled(bool value); + void readMemory(); protected: void memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, uint8_t* data) override; diff --git a/esp_platform.cpp b/esp_platform.cpp index 738f72d..54417f3 100644 --- a/esp_platform.cpp +++ b/esp_platform.cpp @@ -29,7 +29,7 @@ void EspPlatform::macAddress(uint8_t * addr) uint32_t EspPlatform::millis() { - return millis(); + return ::millis(); } void EspPlatform::mdelay(uint32_t millis) @@ -39,6 +39,7 @@ void EspPlatform::mdelay(uint32_t millis) void EspPlatform::restart() { + Serial.println("restart"); ESP.restart(); } @@ -58,7 +59,12 @@ void EspPlatform::setupMultiCast(uint32_t addr, uint16_t port) { _mulitcastAddr = addr; _mulitcastPort = port; - _udp.beginMulticast(WiFi.localIP(), addr, port); + IPAddress mcastaddr(htonl(addr)); + + 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); + Serial.printf("result %d\n", result); } void EspPlatform::closeMultiCast() @@ -66,11 +72,24 @@ void EspPlatform::closeMultiCast() _udp.stop(); } +void printHex(const char* suffix, uint8_t *data, uint8_t length) +{ + Serial.print(suffix); + for (int i = 0; i ",buffer, len); + int result = 0; + result = _udp.beginPacketMulticast(_mulitcastAddr, _mulitcastPort, WiFi.localIP()); + result = _udp.write(buffer, len); + result = _udp.endPacket(); return true; } @@ -82,11 +101,12 @@ int EspPlatform::readBytes(uint8_t * buffer, uint16_t maxLen) if (len > maxLen) { - printf("udp buffer to small. was %d, needed %d\n", maxLen, len); + Serial.printf("udp buffer to small. was %d, needed %d\n", maxLen, len); fatalError(); } _udp.read(buffer, len); + printHex("<- ", buffer, len); return len; } diff --git a/memory.cpp b/memory.cpp index 426b134..702d192 100644 --- a/memory.cpp +++ b/memory.cpp @@ -16,25 +16,34 @@ bool Memory::isMemoryModified() void Memory::readMemory() { - //_data = _platform.getEepromBuffer(512); - //uint8_t* buffer = _data; - //int size = _saveCount; - //for (int i = 0; i < size; i++) - //{ - // buffer = _saveRestores[i]->restore(buffer); - //} + _data = _platform.getEepromBuffer(512); + + if (_data[0] != 0xDE || _data[1] != 0xAD || _data[2] != 0xAF || _data[3] != 0xFE) + return; + + uint8_t* buffer = _data + 4; + int size = _saveCount; + for (int i = 0; i < size; i++) + { + buffer = _saveRestores[i]->restore(buffer); + } } void Memory::writeMemory() { - //uint8_t* buffer = _data; - //int size = _saveCount; - //for (int i = 0; i < size; i++) - //{ - // buffer = _saveRestores[i]->save(buffer); - //} - //_platform.commitToEeprom(); - //_modified = false; + _data[0] = 0xDE; + _data[1] = 0xAD; + _data[2] = 0xAF; + _data[3] = 0xFE; + + uint8_t* buffer = _data + 4; + int size = _saveCount; + for (int i = 0; i < size; i++) + { + buffer = _saveRestores[i]->save(buffer); + } + _platform.commitToEeprom(); + _modified = false; } void Memory::addSaveRestore(SaveRestore * obj) From 7a30d482a532c69af630a9be7eabd708b97749fb Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Tue, 20 Mar 2018 22:04:11 +0100 Subject: [PATCH 03/24] first working version --- address_table_object.cpp | 13 ++-- apdu.cpp | 2 + application_program_object.cpp | 5 +- association_table_object.cpp | 11 +-- bits.cpp | 6 +- bits.h | 124 +++------------------------------ device_object.cpp | 25 +++++-- esp_platform.cpp | 8 +-- group_object.cpp | 18 ++--- group_object_table_object.cpp | 8 +-- ip_parameter_object.cpp | 24 ++++--- network_layer.cpp | 1 + table_object.cpp | 10 +-- 13 files changed, 79 insertions(+), 176 deletions(-) diff --git a/address_table_object.cpp b/address_table_object.cpp index e1bf3d0..50b3dd5 100644 --- a/address_table_object.cpp +++ b/address_table_object.cpp @@ -28,7 +28,7 @@ uint16_t AddressTableObject::entryCount() if (loadState() != LS_LOADED) return 0; - return _groupAddresses[0]; + return ntohs(_groupAddresses[0]); } uint16_t AddressTableObject::getGa(uint16_t tsap) @@ -36,14 +36,14 @@ uint16_t AddressTableObject::getGa(uint16_t tsap) if (loadState() != LS_LOADED || tsap > entryCount() ) return 0; - return _groupAddresses[tsap]; + return ntohs(_groupAddresses[tsap]); } uint16_t AddressTableObject::getTsap(uint16_t addr) { uint16_t size = entryCount(); for (uint16_t i = 1; i <= size; i++) - if (_groupAddresses[i] == addr) + if (ntohs(_groupAddresses[i]) == addr) return i; return 1; } @@ -70,7 +70,7 @@ bool AddressTableObject::contains(uint16_t addr) { uint16_t size = entryCount(); for (uint16_t i = 1; i <= size; i++) - if (_groupAddresses[i] == addr) + if (ntohs(_groupAddresses[i]) == addr) return true; return false; @@ -82,9 +82,4 @@ void AddressTableObject::beforeStateChange(LoadState& newState) return; _groupAddresses = (uint16_t*)_data; - - uint16_t count = reverseByteOrder(_groupAddresses[0]); - // big endian -> little endian - for (size_t i = 0; i <= count; i++) - _groupAddresses[i] = reverseByteOrder(_groupAddresses[i]); } diff --git a/apdu.cpp b/apdu.cpp index 26b86bd..b65daf5 100644 --- a/apdu.cpp +++ b/apdu.cpp @@ -9,6 +9,7 @@ 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) //short apci @@ -18,6 +19,7 @@ ApduType APDU::type() void APDU::type(ApduType atype) { + // ApduType is in big endian so convert to host first, pushWord converts back pushWord((uint16_t)atype, _data); } diff --git a/application_program_object.cpp b/application_program_object.cpp index 4db44ce..1c62b24 100644 --- a/application_program_object.cpp +++ b/application_program_object.cpp @@ -43,7 +43,10 @@ uint8_t ApplicationProgramObject::propertySize(PropertyID id) { switch (id) { - case PID_PROG_VERSION: + case PID_OBJECT_TYPE: + case PID_PEI_TYPE: + return 1; + case PID_PROG_VERSION: return 5; } return TableObject::propertySize(id); diff --git a/association_table_object.cpp b/association_table_object.cpp index c7e21df..89eec6f 100644 --- a/association_table_object.cpp +++ b/association_table_object.cpp @@ -25,7 +25,7 @@ void AssociationTableObject::readProperty(PropertyID id, uint32_t start, uint32_ uint16_t AssociationTableObject::entryCount() { - return _tableData[0]; + return ntohs(_tableData[0]); } uint16_t AssociationTableObject::operator[](uint16_t idx) @@ -33,7 +33,7 @@ uint16_t AssociationTableObject::operator[](uint16_t idx) if (idx < 0 || idx >= entryCount()) return 0; - return _tableData[idx + 1]; + return ntohs(_tableData[idx + 1]); } uint8_t* AssociationTableObject::save(uint8_t* buffer) @@ -53,7 +53,7 @@ int32_t AssociationTableObject::translateAsap(uint16_t asap) uint16_t entries = entryCount(); for (uint16_t i = 0; i < entries; i++) { - uint16_t entry = operator[](i); + uint16_t entry = ntohs(operator[](i)); if (lowByte(entry) == asap) return highByte(entry); } @@ -66,9 +66,4 @@ void AssociationTableObject::beforeStateChange(LoadState& newState) return; _tableData = (uint16_t*)_data; - - uint16_t count = reverseByteOrder(_tableData[0]); - // big endian -> little endian - for (size_t i = 0; i <= count; i++) - _tableData[i] = reverseByteOrder(_tableData[i]); } \ No newline at end of file diff --git a/bits.cpp b/bits.cpp index 3941ab5..380e5f9 100644 --- a/bits.cpp +++ b/bits.cpp @@ -50,7 +50,7 @@ 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[3] = (i & 0xff); data += 4; return data; } @@ -66,10 +66,10 @@ uint8_t* pushByteArray(const uint8_t* src, uint32_t size, uint8_t* data) uint16_t getWord(uint8_t* data) { - return (data[0] << 8) + data[1];; + return (data[0] << 8) + data[1]; } uint32_t getInt(uint8_t * data) { - return (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3];; + return (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]; } diff --git a/bits.h b/bits.h index 73583e1..9f8019c 100644 --- a/bits.h +++ b/bits.h @@ -1,125 +1,19 @@ -/* - * bits.h - Bit and uint8_t manipulation functions. - * - * Copyright (c) 2014 Stefan Taferner - * - * 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 -/** - * Compute the value of the specified bit. - * - * @param bitno - the number of the bit (0, 1, 2... 31) - */ -#define bit(bitno) (1UL << (bitno)) -/** - * Clear the bit of a number. The number can be - * any integer (uint8_t, short, uint32_t, long). - * - * @param val - the number from which to clear the bit - * @param bitno - the number of the bit (0, 1, 2... 31) - */ -#define bitClear(val, bitno) ((val) &= ~(1UL << (bitno))) - -/** - * Set the bit of a number. The number can be - * any integer (uint8_t, short, uint32_t, long). - * - * @param val - the number from which to set the bit - * @param bitno - the number of the bit (0, 1, 2... 31) - */ -#define bitSet(val, bitno) ((val) |= 1UL << (bitno)) - -/** - * Write the value of a bit of a number. - * - * @param val - the number from which to write the bit - * @param bitno - the number of the bit (0, 1, 2... 31) - * @param b - the bit value (0 or 1) - */ -#define bitWrite(val, bitno, b) ((b) ? bitSet(val, bitno) : bitClear(val, bitno)) - -/** - * Read the value of a bit of a number. The number can be - * any integer (uint8_t, short, uint32_t, long). - * - * @param val - the number from which to get the bit - * @param bitno - the number of the bit (0, 1, 2... 31) - * @return The value of the bit (0 or 1). - */ -#define bitRead(val, bitno) (((val) >> (bitno)) & 1) - -/** - * Extract the lowest (rightmost) uint8_t of a number. The number can be - * any integer (uint8_t, short, uint32_t, long). - * - * @param val - the value to extract the lowest uint8_t. - * @return The extracted uint8_t (0..255) - */ +#ifdef __linux__ +#include #define lowByte(val) ((val) & 255) - -/** - * Extract the highest (leftmost) uint8_t of a number. The number can be - * any integer (uint8_t, short, uint32_t, long). - * - * @param val - the value to extract the highest uint8_t. - * @return The extracted uint8_t (0..255) - */ #define highByte(val) (((val) >> ((sizeof(val) - 1) << 3)) & 255) - -/** - * Combine two bytes to a 16 bit uint16_t. - * - * @param high - the high uint8_t. - * @param low - the low uint8_t. - * @return The bytes combined as uint16_t. - */ -uint16_t makeWord(uint8_t high, uint8_t low); - -/** - * Reverse the uint8_t order of an integer. - * - * @param val - the value to reverse. - * @return The value with reversed uint8_t order. - */ - uint32_t reverseByteOrder(uint32_t val); - -/** - * Reverse the uint8_t order of a short integer. - * - * @param val - the value to reverse. - * @return The value with reversed uint8_t order. - */ - uint16_t reverseByteOrder(uint16_t val); +#define bitRead(val, bitno) (((val) >> (bitno)) & 1) +#else +#include +#include +#define printf Serial.printf +#endif -// -// Inline functions -// - -inline uint16_t makeWord(uint8_t high, uint8_t low) -{ - return (high << 8) | low; -} - -inline uint32_t reverseByteOrder(uint32_t val) -{ - uint32_t swapped = ((val >> 24) & 0xff) | // move uint8_t 3 to uint8_t 0 - ((val << 8) & 0xff0000) | // move uint8_t 1 to uint8_t 2 - ((val >> 8) & 0xff00) | // move uint8_t 2 to uint8_t 1 - ((val << 24) & 0xff000000); // uint8_t 0 to uint8_t 3 - return swapped;//__REV(val); -} - -inline uint16_t reverseByteOrder(uint16_t val) -{ - uint16_t swapped = (val >> 8) | (val << 8); - return swapped; -} uint8_t* popByte(uint8_t& b, uint8_t* data); uint8_t* popWord(uint16_t& w, uint8_t* data); diff --git a/device_object.cpp b/device_object.cpp index 5e6286b..3acb3bc 100644 --- a/device_object.cpp +++ b/device_object.cpp @@ -81,12 +81,25 @@ uint8_t DeviceObject::propertySize(PropertyID id) { switch (id) { - case PID_DEVICE_CONTROL: - return 1; - case PID_ROUTING_COUNT: - return 1; - case PID_PROG_MODE: - return 1; + case PID_OBJECT_TYPE: + case PID_DEVICE_CONTROL: + case PID_ROUTING_COUNT: + case PID_PROG_MODE: + case PID_MAX_APDU_LENGTH: + case PID_SUBNET_ADDR: + case PID_DEVICE_ADDR: + return 1; + case PID_MANUFACTURER_ID: + case PID_VERSION: + case PID_DEVICE_DESCRIPTOR: + return 2; + case PID_IO_LIST: + return 4; + case PID_SERIAL_NUMBER: + case PID_HARDWARE_TYPE: + return 6; + case PID_ORDER_INFO: + return 10; } return 0; } diff --git a/esp_platform.cpp b/esp_platform.cpp index 54417f3..4fc866f 100644 --- a/esp_platform.cpp +++ b/esp_platform.cpp @@ -57,9 +57,9 @@ void EspPlatform::fatalError() void EspPlatform::setupMultiCast(uint32_t addr, uint16_t port) { - _mulitcastAddr = addr; + _mulitcastAddr = htonl(addr); _mulitcastPort = port; - IPAddress mcastaddr(htonl(addr)); + IPAddress mcastaddr(_mulitcastAddr); Serial.printf("setup multicast addr: %s port: %d ip: %s\n", mcastaddr.toString().c_str(), port, WiFi.localIP().toString().c_str()); @@ -85,7 +85,7 @@ void printHex(const char* suffix, uint8_t *data, uint8_t length) bool EspPlatform::sendBytes(uint8_t * buffer, uint16_t len) { - printHex("-> ",buffer, len); + printHex("<- ",buffer, len); int result = 0; result = _udp.beginPacketMulticast(_mulitcastAddr, _mulitcastPort, WiFi.localIP()); result = _udp.write(buffer, len); @@ -106,7 +106,7 @@ int EspPlatform::readBytes(uint8_t * buffer, uint16_t maxLen) } _udp.read(buffer, len); - printHex("<- ", buffer, len); + printHex("-> ", buffer, len); return len; } diff --git a/group_object.cpp b/group_object.cpp index 4381b0c..d0e0307 100644 --- a/group_object.cpp +++ b/group_object.cpp @@ -23,7 +23,7 @@ bool GroupObject::responseUpdateEnable() if (!_table) return false; - return bitRead(_table->_tableData[_asap], 15) > 0; + return bitRead(ntohs(_table->_tableData[_asap]), 15) > 0; } bool GroupObject::transmitEnable() @@ -31,7 +31,7 @@ bool GroupObject::transmitEnable() if (!_table) return false; - return bitRead(_table->_tableData[_asap], 14) > 0; + return bitRead(ntohs(_table->_tableData[_asap]), 14) > 0 ; } bool GroupObject::valueReadOnInit() @@ -39,7 +39,7 @@ bool GroupObject::valueReadOnInit() if (!_table) return false; - return bitRead(_table->_tableData[_asap], 13) > 0; + return bitRead(ntohs(_table->_tableData[_asap]), 13) > 0; } bool GroupObject::writeEnable() @@ -47,7 +47,7 @@ bool GroupObject::writeEnable() if (!_table) return false; - return bitRead(_table->_tableData[_asap], 12) > 0; + return bitRead(ntohs(_table->_tableData[_asap]), 12) > 0 ; } bool GroupObject::readEnable() @@ -55,7 +55,7 @@ bool GroupObject::readEnable() if (!_table) return false; - return bitRead(_table->_tableData[_asap], 11) > 0; + return bitRead(ntohs(_table->_tableData[_asap]), 11) > 0; } bool GroupObject::communicationEnable() @@ -63,7 +63,7 @@ bool GroupObject::communicationEnable() if (!_table) return false; - return bitRead(_table->_tableData[_asap], 10) > 0; + return bitRead(ntohs(_table->_tableData[_asap]), 10) > 0; } @@ -72,7 +72,7 @@ Priority GroupObject::priority() if (!_table) return LowPriority; - return (Priority)((_table->_tableData[_asap] >> 6) & (3 << 2)) ; + return (Priority)((ntohs(_table->_tableData[_asap]) >> 6) & (3 << 2)) ; } uint8_t* GroupObject::valueRef() @@ -144,7 +144,7 @@ void GroupObject::commFlag(ComFlag value) int32_t GroupObject::objectReadFloat() { - uint16_t dptValue = makeWord(_data[0], _data[1]); + uint16_t dptValue = getWord(_data); return dptFromFloat(dptValue); } @@ -186,6 +186,6 @@ size_t GroupObject::valueSize() size_t GroupObject::sizeInTelegram() { - uint8_t code = lowByte(_table->_tableData[_asap]); + uint8_t code = lowByte(ntohs(_table->_tableData[_asap])); return asapValueSize(code); } diff --git a/group_object_table_object.cpp b/group_object_table_object.cpp index d9cc83c..7f3c71e 100644 --- a/group_object_table_object.cpp +++ b/group_object_table_object.cpp @@ -27,7 +27,7 @@ uint16_t GroupObjectTableObject::entryCount() if (loadState() != LS_LOADED) return 0; - return _tableData[0]; + return ntohs(_tableData[0]); } @@ -91,10 +91,6 @@ void GroupObjectTableObject::beforeStateChange(LoadState& newState) return; _tableData = (uint16_t*)_data; - uint16_t goCount = reverseByteOrder(_tableData[0]); - // big endian -> little endian - for (size_t i = 0; i <= goCount; i++) - _tableData[i] = reverseByteOrder(_tableData[i]); if (!initGroupObjects()) { @@ -108,7 +104,7 @@ bool GroupObjectTableObject::initGroupObjects() if (!_tableData) return false; - uint16_t goCount = _tableData[0]; + uint16_t goCount = ntohs(_tableData[0]); if (goCount != _groupObjectCount) return false; diff --git a/ip_parameter_object.cpp b/ip_parameter_object.cpp index d7d13e2..52a7c5d 100644 --- a/ip_parameter_object.cpp +++ b/ip_parameter_object.cpp @@ -118,24 +118,28 @@ uint8_t IpParameterObject::propertySize(PropertyID id) { switch (id) { + case PID_IP_ASSIGNMENT_METHOD: + case PID_LOAD_STATE_CONTROL: + case PID_IP_CAPABILITIES: + case PID_TTL: + case PID_KNXNETIP_DEVICE_CAPABILITIES: + case PID_FRIENDLY_NAME: + return 1; + case PID_OBJECT_TYPE: case PID_PROJECT_INSTALLATION_ID: - return 2; case PID_KNX_INDIVIDUAL_ADDRESS: return 2; - case PID_IP_ASSIGNMENT_METHOD: - return 1; + case PID_CURRENT_IP_ADDRESS: + case PID_CURRENT_SUBNET_MASK: + case PID_CURRENT_DEFAULT_GATEWAY: case PID_IP_ADDRESS: - return 4; case PID_SUBNET_MASK: - return 4; case PID_DEFAULT_GATEWAY: - return 4; + case PID_SYSTEM_SETUP_MULTICAST_ADDRESS: case PID_ROUTING_MULTICAST_ADDRESS: return 4; - case PID_TTL: - return 1; - case PID_FRIENDLY_NAME: - return 1; + case PID_MAC_ADDRESS: + return 6; } return 0; } diff --git a/network_layer.cpp b/network_layer.cpp index 83bba34..be4b09c 100644 --- a/network_layer.cpp +++ b/network_layer.cpp @@ -2,6 +2,7 @@ #include "tpdu.h" #include "cemi_frame.h" #include "data_link_layer.h" +#include "bits.h" NetworkLayer::NetworkLayer(TransportLayer& layer): _transportLayer(layer) { diff --git a/table_object.cpp b/table_object.cpp index e5ed445..57fbb3f 100644 --- a/table_object.cpp +++ b/table_object.cpp @@ -47,11 +47,11 @@ uint8_t TableObject::propertySize(PropertyID id) switch (id) { case PID_LOAD_STATE_CONTROL: - return 10; - - //case PID_MCB_TABLE: - // TODO - // break; + return 1; + case PID_TABLE_REFERENCE: + return 4; + case PID_ERROR_CODE: + return 1; } return 0; } From 8b71fb3d8ec7ca2ca8b4487ca416781d64098a51 Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Tue, 20 Mar 2018 23:56:42 +0100 Subject: [PATCH 04/24] add facade --- esp_platform.cpp | 2 +- knx-esp.vcxitems | 3 +- knx-esp.vcxitems.filters | 7 ++- knx_esp.h | 20 +------- knx_facade.cpp | 104 +++++++++++++++++++++++++++++++++++++++ knx_facade.h | 32 ++++++++++++ readme.txt | 19 ------- 7 files changed, 147 insertions(+), 40 deletions(-) create mode 100644 knx_facade.cpp create mode 100644 knx_facade.h delete mode 100644 readme.txt diff --git a/esp_platform.cpp b/esp_platform.cpp index 4fc866f..2ba4115 100644 --- a/esp_platform.cpp +++ b/esp_platform.cpp @@ -40,7 +40,7 @@ void EspPlatform::mdelay(uint32_t millis) void EspPlatform::restart() { Serial.println("restart"); - ESP.restart(); + ESP.reset(); } void EspPlatform::fatalError() diff --git a/knx-esp.vcxitems b/knx-esp.vcxitems index 8228399..caaa10d 100644 --- a/knx-esp.vcxitems +++ b/knx-esp.vcxitems @@ -14,7 +14,6 @@ - @@ -37,6 +36,7 @@ + @@ -65,6 +65,7 @@ + diff --git a/knx-esp.vcxitems.filters b/knx-esp.vcxitems.filters index 82ef4d0..3cf624e 100644 --- a/knx-esp.vcxitems.filters +++ b/knx-esp.vcxitems.filters @@ -77,9 +77,11 @@ Source Files + + Source Files + - Header Files @@ -167,5 +169,8 @@ Header Files + + Header Files + \ No newline at end of file diff --git a/knx_esp.h b/knx_esp.h index d737518..18260d5 100644 --- a/knx_esp.h +++ b/knx_esp.h @@ -1,19 +1,3 @@ -/* - Name: knx_esp.h - Created: 01.03.2018 21:14:55 - Author: tkunze - Editor: http://www.visualmicro.com -*/ - -#ifndef _knx_esp_h -#define _knx_esp_h - -#if defined(ARDUINO) && ARDUINO >= 100 - #include "arduino.h" -#else - #include "WProgram.h" -#endif - - -#endif +#pragma once +#include "knx_facade.h" diff --git a/knx_facade.cpp b/knx_facade.cpp new file mode 100644 index 0000000..258df65 --- /dev/null +++ b/knx_facade.cpp @@ -0,0 +1,104 @@ +#include "knx_facade.h" + +KnxFacade knx; + +KnxFacade::KnxFacade() : _bau(_platform) +{ +} + +bool KnxFacade::enabled() +{ + return _bau.enabled(); +} + +void KnxFacade::enabled(bool value) +{ + _bau.enabled(true); +} + +bool KnxFacade::progMode() +{ + return _bau.deviceObject().progMode(); +} + +void KnxFacade::progMode(bool value) +{ + _bau.deviceObject().progMode(value); +} + +bool KnxFacade::configured() +{ + return _bau.configured(); +} + +void KnxFacade::readMemory() +{ + _bau.readMemory(); +} + +void KnxFacade::loop() +{ + _bau.loop(); +} + +void KnxFacade::registerGroupObjects(GroupObject* groupObjects, uint16_t count) +{ + _bau.groupObjectTable().groupObjects(groupObjects, count); +} + +void KnxFacade::manufacturerId(uint16_t value) +{ + _bau.deviceObject().manufacturerId(value); +} + +void KnxFacade::bauNumber(uint32_t value) +{ + _bau.deviceObject().bauNumber(value); +} + +void KnxFacade::orderNumber(const char* value) +{ + _bau.deviceObject().orderNumber(value); +} + +void KnxFacade::hardwareType(uint8_t* value) +{ + _bau.deviceObject().hardwareType(value); +} + +void KnxFacade::version(uint16_t value) +{ + _bau.deviceObject().version(value); +} + +uint8_t* KnxFacade::paramData(uint32_t addr) +{ + if (!_bau.configured()) + return nullptr; + + return _bau.parameters().data(addr); +} + +uint8_t KnxFacade::paramByte(uint32_t addr) +{ + if (!_bau.configured()) + return 0; + + return _bau.parameters().getByte(addr); +} + +uint16_t KnxFacade::paramWord(uint32_t addr) +{ + if (!_bau.configured()) + return 0; + + return _bau.parameters().getWord(addr); +} + +uint32_t KnxFacade::paramInt(uint32_t addr) +{ + if (!_bau.configured()) + return 0; + + return _bau.parameters().getInt(addr); +} diff --git a/knx_facade.h b/knx_facade.h new file mode 100644 index 0000000..f93a7c0 --- /dev/null +++ b/knx_facade.h @@ -0,0 +1,32 @@ +#pragma once + +#include "esp_platform.h" +#include "bau57B0.h" + +class KnxFacade +{ +public: + KnxFacade(); + bool enabled(); + void enabled(bool value); + bool progMode(); + void progMode(bool value); + bool configured(); + void readMemory(); + void loop(); + void registerGroupObjects(GroupObject* groupObjects, uint16_t count); + void manufacturerId(uint16_t value); + void bauNumber(uint32_t value); + void orderNumber(const char* value); + void hardwareType(uint8_t* value); + void version(uint16_t value); + uint8_t* paramData(uint32_t addr); + uint8_t paramByte(uint32_t addr); + uint16_t paramWord(uint32_t addr); + uint32_t paramInt(uint32_t addr); +private: + EspPlatform _platform; + Bau57B0 _bau; +}; + +extern KnxFacade knx; \ No newline at end of file diff --git a/readme.txt b/readme.txt deleted file mode 100644 index ac2d41e..0000000 --- a/readme.txt +++ /dev/null @@ -1,19 +0,0 @@ -Arduino Compatible Cross Platform C++ Library Project : For more information see http://www.visualmicro.com - -This project works exactly the same way as an Arduino library. - -Add this project to any solution that contains an Arduino project and #include in code as you would any normal Arduino library headers. - -To enable intellisense and to support live build discovery outside of the "standard" Arduino library locations, ensure that the library is added as a shared project reference to the master Arduino project. To do this, right click the master project "References" node and then click "Add Reference". A window will open and the library will appear on the "Shared Projects" tab. Click the checkbox next to the library name to add the reference. If this library is moved the shared referencemust be removed and re-added. - -VS2017 has a bug, workround: After moving existing source code within a "library or shared project", close and re-open the solution. - -Visual Studio will display intellisense for libraries based on the platform/board that has been specified for the currently active "Startup Project" of the current solution. - - -IMPORTANT: The arduino.cc Library Rules must be followed when adding code or restructing libraries. - - - - -blog: http://www.visualmicro.com/post/2017/01/16/Arduino-Cross-Platform-Library-Development.aspx \ No newline at end of file From 82899787b50794ce010672cf0ecb224f57fc32d3 Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Thu, 29 Mar 2018 23:44:53 +0200 Subject: [PATCH 05/24] set 0xfa as default manufacturerId --- knx_facade.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/knx_facade.cpp b/knx_facade.cpp index 258df65..a7c7e88 100644 --- a/knx_facade.cpp +++ b/knx_facade.cpp @@ -4,6 +4,7 @@ KnxFacade knx; KnxFacade::KnxFacade() : _bau(_platform) { + manufacturerId(0xfa); } bool KnxFacade::enabled() From 6257b7f6c45c1a9a06afb4e2d70c87a0bff9115b Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Mon, 9 Apr 2018 22:19:14 +0200 Subject: [PATCH 06/24] add submodule --- .gitmodules | 3 +++ knx | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 knx diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..cd482f2 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "knx"] + path = knx + url = https://github.com/thelsing/knx.git diff --git a/knx b/knx new file mode 160000 index 0000000..96eea4f --- /dev/null +++ b/knx @@ -0,0 +1 @@ +Subproject commit 96eea4ffe87876ded4178a1b025a91943ddfc9c9 From 8015c79d48aab868f5365fb6f5e58991352b37cf Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Mon, 9 Apr 2018 22:38:58 +0200 Subject: [PATCH 07/24] remove submodule --- .gitmodules | 3 --- knx | 1 - 2 files changed, 4 deletions(-) delete mode 160000 knx diff --git a/.gitmodules b/.gitmodules index cd482f2..e69de29 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +0,0 @@ -[submodule "knx"] - path = knx - url = https://github.com/thelsing/knx.git diff --git a/knx b/knx deleted file mode 160000 index 96eea4f..0000000 --- a/knx +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 96eea4ffe87876ded4178a1b025a91943ddfc9c9 From 49deaf4bd11c141d96eba8ca2fd779cf4722530c Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Mon, 9 Apr 2018 22:44:28 +0200 Subject: [PATCH 08/24] move common files to submodule --- .gitmodules | 3 +++ src/knx | 1 + 2 files changed, 4 insertions(+) create mode 160000 src/knx diff --git a/.gitmodules b/.gitmodules index e69de29..6a68cc6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "src/knx"] + path = src/knx + url = https://github.com/thelsing/knx.git diff --git a/src/knx b/src/knx new file mode 160000 index 0000000..2a169ee --- /dev/null +++ b/src/knx @@ -0,0 +1 @@ +Subproject commit 2a169ee66b5a95a11790a18bd1cd9378a49beaab From e99a001ac70664026ba6aef4983e9c84b2dc1d57 Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Mon, 9 Apr 2018 22:46:21 +0200 Subject: [PATCH 09/24] move common files to submodule --- address_table_object.cpp | 85 --- address_table_object.h | 20 - apdu.cpp | 53 -- apdu.h | 22 - application_layer.cpp | 792 ----------------------- application_layer.h | 102 --- application_program_object.cpp | 87 --- application_program_object.h | 20 - association_table_object.cpp | 69 -- association_table_object.h | 20 - bau.cpp | 238 ------- bau.h | 110 ---- bau57B0.cpp | 286 -------- bau57B0.h | 77 --- bits.cpp | 75 --- bits.h | 27 - cemi_frame.cpp | 195 ------ cemi_frame.h | 60 -- data_link_layer.cpp | 199 ------ data_link_layer.h | 33 - datapoint_types.cpp | 63 -- datapoint_types.h | 37 -- device_object.cpp | 253 -------- device_object.h | 46 -- group_object.cpp | 191 ------ group_object.h | 111 ---- group_object_table_object.cpp | 121 ---- group_object_table_object.h | 27 - interface_object.h | 62 -- ip_parameter_object.cpp | 302 --------- ip_parameter_object.h | 42 -- knx_types.h | 103 --- library.properties | 13 +- memory.cpp | 56 -- memory.h | 24 - network_layer.cpp | 127 ---- network_layer.h | 38 -- npdu.cpp | 44 -- npdu.h | 24 - platform.h | 26 - property_types.h | 166 ----- save_restore.h | 10 - esp_platform.cpp => src/esp_platform.cpp | 0 esp_platform.h => src/esp_platform.h | 2 +- knx_esp.h => src/knx_esp.h | 0 knx_facade.cpp => src/knx_facade.cpp | 0 knx_facade.h => src/knx_facade.h | 2 +- table_object.cpp | 257 -------- table_object.h | 33 - tpdu.cpp | 127 ---- tpdu.h | 32 - transport_layer.cpp | 711 -------------------- transport_layer.h | 98 --- 53 files changed, 9 insertions(+), 5709 deletions(-) delete mode 100644 address_table_object.cpp delete mode 100644 address_table_object.h delete mode 100644 apdu.cpp delete mode 100644 apdu.h delete mode 100644 application_layer.cpp delete mode 100644 application_layer.h delete mode 100644 application_program_object.cpp delete mode 100644 application_program_object.h delete mode 100644 association_table_object.cpp delete mode 100644 association_table_object.h delete mode 100644 bau.cpp delete mode 100644 bau.h delete mode 100644 bau57B0.cpp delete mode 100644 bau57B0.h delete mode 100644 bits.cpp delete mode 100644 bits.h delete mode 100644 cemi_frame.cpp delete mode 100644 cemi_frame.h delete mode 100644 data_link_layer.cpp delete mode 100644 data_link_layer.h delete mode 100644 datapoint_types.cpp delete mode 100644 datapoint_types.h delete mode 100644 device_object.cpp delete mode 100644 device_object.h delete mode 100644 group_object.cpp delete mode 100644 group_object.h delete mode 100644 group_object_table_object.cpp delete mode 100644 group_object_table_object.h delete mode 100644 interface_object.h delete mode 100644 ip_parameter_object.cpp delete mode 100644 ip_parameter_object.h delete mode 100644 knx_types.h delete mode 100644 memory.cpp delete mode 100644 memory.h delete mode 100644 network_layer.cpp delete mode 100644 network_layer.h delete mode 100644 npdu.cpp delete mode 100644 npdu.h delete mode 100644 platform.h delete mode 100644 property_types.h delete mode 100644 save_restore.h rename esp_platform.cpp => src/esp_platform.cpp (100%) rename esp_platform.h => src/esp_platform.h (93%) rename knx_esp.h => src/knx_esp.h (100%) rename knx_facade.cpp => src/knx_facade.cpp (100%) rename knx_facade.h => src/knx_facade.h (93%) delete mode 100644 table_object.cpp delete mode 100644 table_object.h delete mode 100644 tpdu.cpp delete mode 100644 tpdu.h delete mode 100644 transport_layer.cpp delete mode 100644 transport_layer.h diff --git a/address_table_object.cpp b/address_table_object.cpp deleted file mode 100644 index 50b3dd5..0000000 --- a/address_table_object.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include - -#include "address_table_object.h" -#include "bits.h" - -using namespace std; - - -AddressTableObject::AddressTableObject(uint8_t* memoryReference): TableObject(memoryReference) -{ - -} - -void AddressTableObject::readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data) -{ - switch (id) - { - case PID_OBJECT_TYPE: - pushWord(OT_ADDR_TABLE, data); - break; - default: - TableObject::readProperty(id, start, count, data); - } -} - -uint16_t AddressTableObject::entryCount() -{ - if (loadState() != LS_LOADED) - return 0; - - return ntohs(_groupAddresses[0]); -} - -uint16_t AddressTableObject::getGa(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(); - for (uint16_t i = 1; i <= size; i++) - if (ntohs(_groupAddresses[i]) == addr) - return i; - return 1; -} - -#pragma region SaveRestore - -uint8_t* AddressTableObject::save(uint8_t* buffer) -{ - return TableObject::save(buffer); -} - -uint8_t* AddressTableObject::restore(uint8_t* buffer) -{ - buffer = TableObject::restore(buffer); - - _groupAddresses = (uint16_t*)_data; - - return buffer; -} - -#pragma endregion - -bool AddressTableObject::contains(uint16_t addr) -{ - uint16_t size = entryCount(); - for (uint16_t i = 1; i <= size; i++) - if (ntohs(_groupAddresses[i]) == addr) - return true; - - return false; -} - -void AddressTableObject::beforeStateChange(LoadState& newState) -{ - if (newState != LS_LOADED) - return; - - _groupAddresses = (uint16_t*)_data; -} diff --git a/address_table_object.h b/address_table_object.h deleted file mode 100644 index c7c6022..0000000 --- a/address_table_object.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "table_object.h" - -class AddressTableObject: public TableObject -{ -public: - AddressTableObject(uint8_t* memoryReference); - void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data); - uint16_t entryCount(); - uint16_t getGa(uint16_t tsap); - uint16_t getTsap(uint16_t ga); - uint8_t* save(uint8_t* buffer); - uint8_t* restore(uint8_t* buffer); - bool contains(uint16_t addr); -protected: - virtual void beforeStateChange(LoadState& newState); -private: - uint16_t* _groupAddresses; -}; \ No newline at end of file diff --git a/apdu.cpp b/apdu.cpp deleted file mode 100644 index b65daf5..0000000 --- a/apdu.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#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) //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.print("APDU: "); - //print.print(type(), HEX, 4); - //print.print(" "); - //print.print(_data[0] & 0x3, HEX, 2); - //for (uint8_t i = 1; i < length() + 1; ++i) - //{ - // if (i) print.print(" "); - // print.print(_data[i], HEX, 2); - //} - //print.println(); -} diff --git a/apdu.h b/apdu.h deleted file mode 100644 index e729783..0000000 --- a/apdu.h +++ /dev/null @@ -1,22 +0,0 @@ -#pragma once - -#include -#include "knx_types.h" - -class CemiFrame; - -class APDU -{ - friend class CemiFrame; -public: - APDU(uint8_t* data, CemiFrame& frame); - ApduType type(); - void type(ApduType atype); - uint8_t* data(); - CemiFrame& frame(); - uint8_t length() const; - void printPDU(); -private: - uint8_t* _data; - CemiFrame& _frame; -}; \ No newline at end of file diff --git a/application_layer.cpp b/application_layer.cpp deleted file mode 100644 index 3cc3c8e..0000000 --- a/application_layer.cpp +++ /dev/null @@ -1,792 +0,0 @@ -#include "application_layer.h" -#include "transport_layer.h" -#include "cemi_frame.h" -#include "association_table_object.h" -#include "apdu.h" -#include "bau.h" -#include "string.h" -#include "bits.h" -#include - -ApplicationLayer::ApplicationLayer(AssociationTableObject& assocTable, BusAccessUnit& bau): - _assocTable(assocTable), _bau(bau) -{ -} - -void ApplicationLayer::transportLayer(TransportLayer& layer) -{ - _transportLayer = &layer; -} - -#pragma region TL Callbacks - -void ApplicationLayer::dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) -{ - uint16_t entries = _assocTable.entryCount(); - - uint8_t len = apdu.length(); - uint8_t dataArray[len]; - uint8_t* data = dataArray; - memcpy(data, apdu.data(), len); - if (len == 1) - { - //less than six bit are encoded in first byte - *data &= 0x3f; - } - else - { - data += 1; - len -= 1; - } - - for (uint16_t i = 0; i < entries; i++) - { - uint16_t entry = _assocTable[i]; - if (highByte(entry) == tsap) - { - uint16_t asap = lowByte(entry); - switch (apdu.type()) - { - case GroupValueRead: - _bau.groupValueReadIndication(asap, priority, hopType); - break; - case GroupValueResponse: - _bau.groupValueReadAppLayerConfirm(asap, priority, hopType, data, len); - break; - case GroupValueWrite: - _bau.groupValueWriteIndication(asap, priority, hopType, data, len); - } - } - } -} - -void ApplicationLayer::dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status) -{ - switch (apdu.type()) - { - case GroupValueRead: - _bau.groupValueReadLocalConfirm(ack, _savedAsapReadRequest, priority, hopType, status); - break; - case GroupValueResponse: - _bau.groupValueReadResponseConfirm(ack, _savedAsapResponse, priority, hopType, apdu.data(), apdu.length() - 1, status); - break; - case GroupValueWrite: - _bau.groupValueWriteLocalConfirm(ack, _savedAsapWriteRequest, priority, hopType, apdu.data(), apdu.length() - 1, status); - break; - } -} - -void ApplicationLayer::dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) -{ - uint8_t* data = apdu.data(); - switch (apdu.type()) - { - case IndividualAddressWrite: - { - uint16_t newAddress; - popWord(newAddress, data + 1); - _bau.individualAddressWriteIndication(hopType, newAddress); - break; - } - case IndividualAddressRead: - _bau.individualAddressReadIndication(hopType); - break; - case IndividualAddressResponse: - _bau.individualAddressReadAppLayerConfirm(hopType, apdu.frame().sourceAddress()); - break; - case IndividualAddressSerialNumberRead: - _bau.individualAddressSerialNumberReadIndication(hopType, data + 1); - break; - case IndividualAddressSerialNumberResponse: - { - uint16_t domainAddress; - popWord(domainAddress, data + 7); - _bau.individualAddressSerialNumberReadAppLayerConfirm(hopType, data + 1, apdu.frame().sourceAddress(), - domainAddress); - break; - } - case IndividualAddressSerialNumberWrite: - { - uint16_t newAddress; - popWord(newAddress, data + 7); - _bau.individualAddressSerialNumberWriteIndication(hopType, data + 1, newAddress); - break; - } - } -} - -void ApplicationLayer::dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, bool status) -{ - uint8_t* data = apdu.data(); - switch (apdu.type()) - { - case IndividualAddressWrite: - { - uint16_t newAddress; - popWord(newAddress, data + 1); - _bau.individualAddressWriteLocalConfirm(ack, hopType, newAddress, status); - break; - } - case IndividualAddressRead: - _bau.individualAddressReadLocalConfirm(ack, hopType, status); - break; - case IndividualAddressResponse: - _bau.individualAddressReadResponseConfirm(ack, hopType, status); - break; - case IndividualAddressSerialNumberRead: - _bau.individualAddressSerialNumberReadLocalConfirm(ack, hopType, data + 1, status); - break; - case IndividualAddressSerialNumberResponse: - { - uint16_t domainAddress; - popWord(domainAddress, data + 7); - _bau.individualAddressSerialNumberReadResponseConfirm(ack, hopType, data + 1, domainAddress, status); - break; - } - case IndividualAddressSerialNumberWrite: - { - uint16_t newAddress; - popWord(newAddress, data + 7); - _bau.individualAddressSerialNumberWriteLocalConfirm(ack, hopType, data + 1, newAddress, status); - break; - } - } -} - -void ApplicationLayer::dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu) -{ - -} - -void ApplicationLayer::dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status) -{ - -} - -void ApplicationLayer::dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) -{ - individualIndication(hopType, priority, tsap, apdu); -} - -void ApplicationLayer::dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status) -{ - individualConfirm(ack, hopType, priority, tsap, apdu, status); -} - -void ApplicationLayer::connectIndication(uint16_t tsap) -{ - _connectedTsap = tsap; -} - -void ApplicationLayer::connectConfirm(uint16_t destination, uint16_t tsap, bool status) -{ - -} - -void ApplicationLayer::disconnectIndication(uint16_t tsap) -{ - _connectedTsap = -1; -} - -void ApplicationLayer::disconnectConfirm(Priority priority, uint16_t tsap, bool status) -{ - -} - -void ApplicationLayer::dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu) -{ - individualIndication(NetworkLayerParameter, priority, tsap, apdu); -} - -void ApplicationLayer::dataConnectedConfirm(uint16_t tsap) -{ - -} -#pragma endregion -void ApplicationLayer::groupValueReadRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType) -{ - _savedAsapReadRequest = asap; - CemiFrame frame(1); - APDU& apdu = frame.apdu(); - apdu.type(GroupValueRead); - - int32_t value = _assocTable.translateAsap(asap); - if (value < 0) - return; // there is no tsap in association table for this asap - - uint16_t tsap = (uint16_t)value; - - // first to bus then to itself - _transportLayer->dataGroupRequest(ack, hopType, priority, tsap, apdu); - dataGroupIndication(hopType, priority, tsap, apdu); -} - -void ApplicationLayer::groupValueReadResponse(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength) -{ - _savedAsapResponse = asap; - groupValueSend(GroupValueResponse, ack, asap, priority, hopType, data, dataLength); -} - -void ApplicationLayer::groupValueWriteRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength) -{ - _savedAsapWriteRequest = asap; - groupValueSend(GroupValueWrite, ack, asap, priority, hopType, data, dataLength); -} - -void ApplicationLayer::individualAddressWriteRequest(AckType ack, HopCountType hopType, uint16_t newaddress) -{ - CemiFrame frame(3); - APDU& apdu = frame.apdu(); - apdu.type(IndividualAddressWrite); - uint8_t* apduData = apdu.data(); - pushWord(newaddress, apduData + 1); - _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); -} - -void ApplicationLayer::individualAddressReadRequest(AckType ack, HopCountType hopType) -{ - CemiFrame frame(1); - APDU& apdu = frame.apdu(); - apdu.type(IndividualAddressRead); - _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); -} - -void ApplicationLayer::individualAddressReadResponse(AckType ack, HopCountType hopType) -{ - CemiFrame frame(1); - APDU& apdu = frame.apdu(); - apdu.type(IndividualAddressResponse); - _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); -} - -void ApplicationLayer::individualAddressSerialNumberReadRequest(AckType ack, HopCountType hopType, uint8_t * serialNumber) -{ - CemiFrame frame(7); - APDU& apdu = frame.apdu(); - apdu.type(IndividualAddressSerialNumberRead); - uint8_t* data = apdu.data() + 1; - memcpy(data, serialNumber, 6); - _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); -} - -void ApplicationLayer::individualAddressSerialNumberReadResponse(AckType ack, HopCountType hopType, - uint8_t * serialNumber, uint16_t domainAddress) -{ - CemiFrame frame(7); - APDU& apdu = frame.apdu(); - apdu.type(IndividualAddressSerialNumberResponse); - uint8_t* data = apdu.data() + 1; - memcpy(data, serialNumber, 6); - data += 6; - pushWord(domainAddress, data); - _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); -} - -void ApplicationLayer::individualAddressSerialNumberWriteRequest(AckType ack, HopCountType hopType, uint8_t * serialNumber, - uint16_t newaddress) -{ - CemiFrame frame(13); - APDU& apdu = frame.apdu(); - apdu.type(IndividualAddressSerialNumberWrite); - uint8_t* data = apdu.data() + 1; - memcpy(data, serialNumber, 6); - data += 6; - pushWord(newaddress, data); - _transportLayer->dataBroadcastRequest(ack, hopType, SystemPriority, apdu); -} - -void ApplicationLayer::deviceDescriptorReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t descriptorType) -{ - CemiFrame frame(1); - APDU& apdu = frame.apdu(); - apdu.type(DeviceDescriptorRead); - uint8_t* data = apdu.data(); - *data |= (descriptorType & 0x3f); - - individualSend(ack, hopType, priority, asap, apdu); -} - -void ApplicationLayer::deviceDescriptorReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t descriptorType, uint8_t* deviceDescriptor) -{ - uint8_t length = 0; - switch (descriptorType) - { - case 0: - length = 3; - break; - case 2: - length = 14; - break; - default: - length = 1; - descriptorType = 0x3f; - break; - } - CemiFrame frame(length); - APDU& apdu = frame.apdu(); - apdu.type(DeviceDescriptorResponse); - uint8_t* data = apdu.data(); - *data |= (descriptorType & 0x3f); - - if (length > 1) - memcpy(data + 1, deviceDescriptor, length - 1); - - individualSend(ack, hopType, priority, asap, apdu); -} - -void ApplicationLayer::restartRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap) -{ - CemiFrame frame(1); - APDU& apdu = frame.apdu(); - apdu.type(Restart); - - individualSend(ack, hopType, priority, asap, apdu); -} - -void ApplicationLayer::propertyValueReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex) -{ - CemiFrame frame(5); - APDU& apdu = frame.apdu(); - apdu.type(PropertyValueRead); - uint8_t* data = apdu.data(); - data += 1; - data = pushByte(objectIndex, data); - data = pushByte(propertyId, data); - pushWord(startIndex & 0xfff, data); - *data &= ((numberOfElements & 0xf) << 4); - - individualSend(ack, hopType, priority, asap, apdu); -} - -void ApplicationLayer::propertyValueReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) -{ - propertyDataSend(PropertyValueResponse, ack, priority, hopType, asap, objectIndex, propertyId, numberOfElements, - startIndex, data, length); -} - -void ApplicationLayer::propertyValueWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t * data, uint8_t length) -{ - propertyDataSend(PropertyValueWrite, ack, priority, hopType, asap, objectIndex, propertyId, numberOfElements, - startIndex, data, length); -} - -void ApplicationLayer::propertyDescriptionReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex) -{ - CemiFrame frame(4); - APDU& apdu = frame.apdu(); - apdu.type(PropertyDescriptionRead); - uint8_t* data = apdu.data(); - data[1] = objectIndex; - data[2] = propertyId; - data[3] = propertyIndex; - individualSend(ack, hopType, priority, asap, apdu); -} - -void ApplicationLayer::propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, - uint16_t maxNumberOfElements, uint8_t access) -{ - CemiFrame frame(8); - APDU& apdu = frame.apdu(); - apdu.type(PropertyDescriptionRead); - uint8_t* data = apdu.data(); - data[1] = objectIndex; - data[2] = propertyId; - data[3] = propertyIndex; - if (writeEnable) - data[4] |= 0x80; - data[4] |= (type & 0x3f); - pushWord(maxNumberOfElements & 0xfff, data + 5); - data[7] = access; - individualSend(ack, hopType, priority, asap, apdu); -} - -void ApplicationLayer::memoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint16_t memoryAddress) -{ - CemiFrame frame(3); - APDU& apdu = frame.apdu(); - apdu.type(MemoryRead); - uint8_t* data = apdu.data(); - *data |= (number & 0x3f); - pushWord(memoryAddress, data + 1); - individualSend(ack, hopType, priority, asap, apdu); -} - -void ApplicationLayer::memoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint16_t memoryAddress, uint8_t * memoryData) -{ - memorySend(MemoryResponse, ack, priority, hopType, asap, number, memoryAddress, memoryData); -} - -void ApplicationLayer::memoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t number, uint16_t memoryAddress, uint8_t * data) -{ - memorySend(MemoryWrite, ack, priority, hopType, asap, number, memoryAddress, data); -} - -void ApplicationLayer::userMemoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t number, uint32_t memoryAddress) -{ - CemiFrame frame(4); - APDU& apdu = frame.apdu(); - apdu.type(UserMemoryRead); - uint8_t* data = apdu.data(); - data[1] |= (number & 0xf); - data[1] |= ((memoryAddress >> 12) & 0xf0); - pushWord(memoryAddress & 0xff, data + 2); - individualSend(ack, hopType, priority, asap, apdu); -} - -void ApplicationLayer::userMemoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t number, uint32_t memoryAddress, uint8_t * memoryData) -{ - userMemorySend(UserMemoryResponse, ack, priority, hopType, asap, number, memoryAddress, memoryData); -} - -void ApplicationLayer::userMemoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t number, uint32_t memoryAddress, uint8_t * memoryData) -{ - userMemorySend(UserMemoryWrite, ack, priority, hopType, asap, number, memoryAddress, memoryData); -} - -void ApplicationLayer::userManufacturerInfoReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap) -{ - CemiFrame frame(1); - APDU& apdu = frame.apdu(); - apdu.type(UserManufacturerInfoRead); - individualSend(ack, hopType, priority, asap, apdu); -} - -void ApplicationLayer::userManufacturerInfoReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t* info) -{ - CemiFrame frame(4); - APDU& apdu = frame.apdu(); - apdu.type(UserMemoryRead); - uint8_t* data = apdu.data(); - memcpy(data + 1, info, 3); - individualSend(ack, hopType, priority, asap, apdu); -} - -void ApplicationLayer::authorizeRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint32_t key) -{ - CemiFrame frame(6); - APDU& apdu = frame.apdu(); - apdu.type(AuthorizeRequest); - uint8_t* data = apdu.data(); - pushInt(key, data + 2); - individualSend(ack, hopType, priority, asap, apdu); -} - -void ApplicationLayer::authorizeResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level) -{ - CemiFrame frame(2); - APDU& apdu = frame.apdu(); - apdu.type(AuthorizeResponse); - uint8_t* data = apdu.data(); - data[1] = level; - individualSend(ack, hopType, priority, asap, apdu); -} - -void ApplicationLayer::keyWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, uint32_t key) -{ - CemiFrame frame(6); - APDU& apdu = frame.apdu(); - apdu.type(KeyWrite); - uint8_t* data = apdu.data(); - data[1] = level; - pushInt(key, data + 2); - individualSend(ack, hopType, priority, asap, apdu); -} - -void ApplicationLayer::keyWriteResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level) -{ - CemiFrame frame(6); - APDU& apdu = frame.apdu(); - apdu.type(KeyResponse); - uint8_t* data = apdu.data(); - data[1] = level; - individualSend(ack, hopType, priority, asap, apdu); -} - -void ApplicationLayer::propertyDataSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) -{ - CemiFrame frame(5 + length); - APDU& apdu = frame.apdu(); - apdu.type(type); - uint8_t* apduData = apdu.data(); - apduData += 1; - apduData = pushByte(objectIndex, apduData); - apduData = pushByte(propertyId, apduData); - pushWord(startIndex & 0xfff, apduData); - *apduData |= ((numberOfElements & 0xf) << 4); - apduData += 2; - if (length > 0) - memcpy(apduData, data, length); - - if (asap == _connectedTsap) - _transportLayer->dataConnectedRequest(asap, priority, apdu); - else - _transportLayer->dataIndividualRequest(ack, hopType, priority, asap, apdu); -} - -void ApplicationLayer::groupValueSend(ApduType type, AckType ack, uint16_t asap, Priority priority, HopCountType hopType, - uint8_t* data, uint8_t& dataLength) -{ - CemiFrame frame(dataLength + 1); - APDU& apdu = frame.apdu(); - apdu.type(type); - uint8_t* apdudata = apdu.data(); - if (dataLength == 0) - { - // data size is six bit or less. So store int first byte - *apdudata &= ~0x3f; - *apdudata |= (*data & 0x3f); - } - else - { - memcpy(apdudata + 1, data, dataLength); - } - // no need to check if there is a tsap. This is a response, so the read got trough - uint16_t tsap = (uint16_t)_assocTable.translateAsap(asap); - _transportLayer->dataGroupRequest(ack, hopType, priority, tsap, apdu); - dataGroupIndication(hopType, priority, tsap, apdu); -} - -void ApplicationLayer::memorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint16_t memoryAddress, uint8_t * memoryData) -{ - CemiFrame frame(3 + number); - APDU& apdu = frame.apdu(); - apdu.type(type); - uint8_t* data = apdu.data(); - *data |= (number & 0x3f); - pushWord(memoryAddress, data + 1); - if (number > 0) - memcpy(data + 3, memoryData, number); - - individualSend(ack, hopType, priority, asap, apdu); -} - -void ApplicationLayer::userMemorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint32_t memoryAddress, uint8_t * memoryData) -{ - CemiFrame frame(4 + number); - APDU& apdu = frame.apdu(); - apdu.type(type); - uint8_t* data = apdu.data(); - data[1] |= (number & 0xf); - data[1] |= ((memoryAddress >> 12) & 0xf0); - pushWord(memoryAddress & 0xffff, data + 2); - if (number > 0) - memcpy(data + 4, memoryData, number); - individualSend(ack, hopType, priority, asap, apdu); -} - -void ApplicationLayer::individualIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU & apdu) -{ - uint8_t* data = apdu.data(); - switch (apdu.type()) - { - case DeviceDescriptorRead: - _bau.deviceDescriptorReadIndication(priority, hopType, tsap, *data & 0x3f); - break; - case DeviceDescriptorResponse: - _bau.deviceDescriptorReadAppLayerConfirm(priority, hopType, tsap, *data & 0x3f, data + 1); - break; - case Restart: - if ((*data & 0x3f) == 0) - _bau.restartRequestIndication(priority, hopType, tsap); - break; - case PropertyValueRead: - { - uint16_t startIndex; - popWord(startIndex, data + 3); - startIndex &= 0xfff; - _bau.propertyValueReadIndication(priority, hopType, tsap, data[1], data[2], data[3] >> 4, startIndex); - break; - } - case PropertyValueResponse: - { - uint16_t startIndex; - popWord(startIndex, data + 3); - startIndex &= 0xfff; - _bau.propertyValueReadAppLayerConfirm(priority, hopType, tsap, data[1], data[2], data[3] >> 4, - startIndex, data + 5, apdu.length() - 5); - break; - } - case PropertyValueWrite: - { - uint16_t startIndex; - popWord(startIndex, data + 3); - startIndex &= 0xfff; - _bau.propertyValueWriteIndication(priority, hopType, tsap, data[1], data[2], data[3] >> 4, - startIndex, data + 5, apdu.length() - 5); - break; - } - case PropertyDescriptionRead: - _bau.propertyDescriptionReadIndication(priority, hopType, tsap, data[1], data[2], data[3]); - break; - case PropertyDescriptionResponse: - _bau.propertyDescriptionReadAppLayerConfirm(priority, hopType, tsap, data[1], data[2], data[3], - (data[4] & 0x80) > 0, data[4] & 0x3f, getWord(data + 5) & 0xfff, data[7]); - break; - case MemoryRead: - _bau.memoryReadIndication(priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1)); - break; - case MemoryResponse: - _bau.memoryReadAppLayerConfirm(priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), data + 3); - break; - case MemoryWrite: - _bau.memoryWriteIndication(priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), data + 3); - break; - case UserMemoryRead: - { - uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; - _bau.userMemoryReadIndication(priority, hopType, tsap, data[1] & 0xf, address); - break; - } - case UserMemoryResponse: - { - uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; - _bau.userMemoryReadAppLayerConfirm(priority, hopType, tsap, data[1] & 0xf, address, data + 4); - break; - } - case UserMemoryWrite: - { - uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; - _bau.userMemoryWriteIndication(priority, hopType, tsap, data[1] & 0xf, address, data + 4); - break; - } - case UserManufacturerInfoRead: - _bau.userManufacturerInfoIndication(priority, hopType, tsap); - break; - case UserManufacturerInfoResponse: - _bau.userManufacturerInfoAppLayerConfirm(priority, hopType, tsap, data + 1); - break; - case AuthorizeRequest: - _bau.authorizeIndication(priority, hopType, tsap, getInt(data + 2)); - break; - case AuthorizeResponse: - _bau.authorizeAppLayerConfirm(priority, hopType, tsap, data[1]); - break; - case KeyWrite: - _bau.keyWriteIndication(priority, hopType, tsap, data[1], getInt(data + 2)); - break; - case KeyResponse: - _bau.keyWriteAppLayerConfirm(priority, hopType, tsap, data[1]); - break; - } -} - -void ApplicationLayer::individualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU & apdu, bool status) -{ - uint8_t* data = apdu.data(); - switch (apdu.type()) - { - case DeviceDescriptorRead: - _bau.deviceDescriptorReadLocalConfirm(ack, priority, hopType, tsap, *data & 0x3f, status); - break; - case DeviceDescriptorResponse: - _bau.deviceDescriptorReadResponseConfirm(ack, priority, hopType, tsap, *data & 0x3f, data + 1, status); - break; - case Restart: - _bau.restartRequestLocalConfirm(ack, priority, hopType, tsap, status); - break; - case PropertyValueRead: - { - uint16_t startIndex; - popWord(startIndex, data + 3); - startIndex &= 0xfff; - _bau.propertyValueReadLocalConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3] >> 4, - startIndex, status); - break; - } - case PropertyValueResponse: - { - uint16_t startIndex; - popWord(startIndex, data + 3); - startIndex &= 0xfff; - _bau.propertyValueReadResponseConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3] >> 4, - startIndex, data + 5, apdu.length() - 5, status); - break; - } - case PropertyValueWrite: - { - uint16_t startIndex; - popWord(startIndex, data + 3); - startIndex &= 0xfff; - _bau.propertyValueWriteLocalConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3] >> 4, - startIndex, data + 5, apdu.length() - 5, status); - break; - } - case PropertyDescriptionRead: - _bau.propertyDescriptionReadLocalConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3], status); - break; - case PropertyDescriptionResponse: - _bau.propertyDescriptionReadResponseConfirm(ack, priority, hopType, tsap, data[1], data[2], data[3], - (data[4] & 0x80) > 0, data[4] & 0x3f, getWord(data + 5) & 0xfff, data[7], status); - break; - case MemoryRead: - _bau.memoryReadLocalConfirm(ack, priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), status); - break; - case MemoryResponse: - _bau.memoryReadResponseConfirm(ack, priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), data + 3, status); - break; - case MemoryWrite: - _bau.memoryWriteLocalConfirm(ack, priority, hopType, tsap, data[0] & 0x3f, getWord(data + 1), data + 3, status); - break; - case UserMemoryRead: - { - uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; - _bau.memoryReadLocalConfirm(ack, priority, hopType, tsap, data[1] & 0xf, address, status); - break; - } - case UserMemoryResponse: - { - uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; - _bau.memoryReadResponseConfirm(ack, priority, hopType, tsap, data[1] & 0xf, address, data + 4, status); - break; - } - case UserMemoryWrite: - { - uint32_t address = ((data[1] & 0xf0) << 12) + (data[2] << 8) + data[3]; - _bau.memoryWriteLocalConfirm(ack, priority, hopType, tsap, data[1] & 0xf, address, data + 4, status); - break; - } - case UserManufacturerInfoRead: - _bau.userManufacturerInfoLocalConfirm(ack, priority, hopType, tsap, status); - break; - case UserManufacturerInfoResponse: - _bau.userManufacturerInfoResponseConfirm(ack, priority, hopType, tsap, data + 1, status); - break; - case AuthorizeRequest: - _bau.authorizeLocalConfirm(ack, priority, hopType, tsap, getInt(data + 2), status); - break; - case AuthorizeResponse: - _bau.authorizeResponseConfirm(ack, priority, hopType, tsap, data[1], status); - break; - case KeyWrite: - _bau.keyWriteLocalConfirm(ack, priority, hopType, tsap, data[1], getInt(data + 2), status); - break; - case KeyResponse: - _bau.keyWriteResponseConfirm(ack, priority, hopType, tsap, data[1], status); - break; - } -} - -void ApplicationLayer::individualSend(AckType ack, HopCountType hopType, Priority priority, uint16_t asap, APDU& apdu) -{ - if (asap == _connectedTsap) - _transportLayer->dataConnectedRequest(asap, priority, apdu); - else - _transportLayer->dataIndividualRequest(ack, hopType, priority, asap, apdu); -} diff --git a/application_layer.h b/application_layer.h deleted file mode 100644 index bd49216..0000000 --- a/application_layer.h +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once - -#include -#include "knx_types.h" -#include "apdu.h" - -class AssociationTableObject; -class BusAccessUnit; -class TransportLayer; - -class ApplicationLayer -{ -public: - ApplicationLayer(AssociationTableObject& assocTable, BusAccessUnit& bau); - void transportLayer(TransportLayer& layer); - - // from transport layer -#pragma region Transport-Layer-Callbacks - void dataGroupIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu); - void dataGroupConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, - APDU& apdu, bool status); - void dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu); - void dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, APDU& apdu, bool status); - void dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu); - void dataSystemBroadcastConfirm(HopCountType hopType, Priority priority, APDU& apdu, bool status); - void dataIndividualIndication(HopCountType hopType, Priority priority, uint16_t source, APDU& apdu); - void dataIndividualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status); - 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); - void dataConnectedIndication(Priority priority, uint16_t tsap, APDU& apdu); - void dataConnectedConfirm(uint16_t tsap); -#pragma endregion - - void groupValueReadRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType); - void groupValueReadResponse(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, uint8_t dataLength); - void groupValueWriteRequest(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, uint8_t dataLength); - void individualAddressWriteRequest(AckType ack, HopCountType hopType, uint16_t newaddress); - void individualAddressReadRequest(AckType ack, HopCountType hopType); - void individualAddressReadResponse(AckType ack, HopCountType hopType); - void individualAddressSerialNumberReadRequest(AckType ack, HopCountType hopType, uint8_t* serialNumber); - void individualAddressSerialNumberReadResponse(AckType ack, HopCountType hopType, uint8_t* serialNumber, - uint16_t domainAddress); - void individualAddressSerialNumberWriteRequest(AckType ack, HopCountType hopType, uint8_t* serialNumber, - uint16_t newaddress); - void deviceDescriptorReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t descriptorType); - void deviceDescriptorReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t descriptorType, uint8_t* deviceDescriptor); - void restartRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap); - void propertyValueReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex); - void propertyValueReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, - uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length); - void propertyValueWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, - uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length); - void propertyDescriptionReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex); - void propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool writeEnable, uint8_t type, - uint16_t maxNumberOfElements, uint8_t access); - void memoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint16_t memoryAddress); - void memoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint16_t memoryAddress, uint8_t* data); - void memoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint16_t memoryAddress, uint8_t* data); - void userMemoryReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint32_t memoryAddress); - void userMemoryReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint32_t memoryAddress, uint8_t* memoryData); - void userMemoryWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint32_t memoryAddress, uint8_t* memoryData); - void userManufacturerInfoReadRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap); - void userManufacturerInfoReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t* info); - void authorizeRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint32_t key); - void authorizeResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level); - void keyWriteRequest(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, uint32_t key); - void keyWriteResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level); -private: - void propertyDataSend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t objectIndex, 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, uint8_t number, - uint16_t memoryAddress, uint8_t * memoryData); - void userMemorySend(ApduType type, AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t number, uint32_t memoryAddress, uint8_t* memoryData); - void groupValueSend(ApduType type, AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, uint8_t& dataLength); - void individualIndication(HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu); - void individualConfirm(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu, bool status); - void individualSend(AckType ack, HopCountType hopType, Priority priority, uint16_t asap, APDU& apdu); - - uint16_t _savedAsapReadRequest; - uint16_t _savedAsapWriteRequest; - uint16_t _savedAsapResponse; - AssociationTableObject& _assocTable; - BusAccessUnit& _bau; - TransportLayer* _transportLayer; - int32_t _connectedTsap; -}; diff --git a/application_program_object.cpp b/application_program_object.cpp deleted file mode 100644 index 1c62b24..0000000 --- a/application_program_object.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "application_program_object.h" -#include "bits.h" - -ApplicationProgramObject::ApplicationProgramObject(uint8_t* memoryReference): TableObject(memoryReference) -{ - -} - -void ApplicationProgramObject::readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data) -{ - switch (id) - { - case PID_OBJECT_TYPE: - pushWord(OT_APPLICATION_PROG, data); - break; - case PID_PROG_VERSION: - pushByteArray(_programVersion, 5, data); - break; - case PID_PEI_TYPE: - pushByte(0x0, data); - break; - default: - TableObject::readProperty(id, start, count, data); - } -} - -void ApplicationProgramObject::writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count) -{ - switch (id) - { - case PID_PROG_VERSION: - for (uint32_t i = 0; i < 5; i++) - { - _programVersion[i] = data[i]; - } - break; - default: - TableObject::writeProperty(id, start, data, count); - } -} - -uint8_t ApplicationProgramObject::propertySize(PropertyID id) -{ - switch (id) - { - case PID_OBJECT_TYPE: - case PID_PEI_TYPE: - return 1; - case PID_PROG_VERSION: - return 5; - } - return TableObject::propertySize(id); -} - -uint8_t * ApplicationProgramObject::data(uint32_t addr) -{ - return _data + addr; -} - -uint8_t ApplicationProgramObject::getByte(uint32_t addr) -{ - return *(_data + addr); -} - -uint16_t ApplicationProgramObject::getWord(uint32_t addr) -{ - return ::getWord(_data + addr); -} - -uint32_t ApplicationProgramObject::getInt(uint32_t addr) -{ - return ::getInt(_data + addr); -} - -uint8_t* ApplicationProgramObject::save(uint8_t* buffer) -{ - buffer = pushByteArray(_programVersion, 5, buffer); - - return TableObject::save(buffer); -} - -uint8_t* ApplicationProgramObject::restore(uint8_t* buffer) -{ - buffer = popByteArray(_programVersion, 5, buffer); - - return TableObject::restore(buffer); -} diff --git a/application_program_object.h b/application_program_object.h deleted file mode 100644 index da4caf0..0000000 --- a/application_program_object.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "table_object.h" - -class ApplicationProgramObject: public TableObject -{ -public: - ApplicationProgramObject(uint8_t* memoryReference); - void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data); - void writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count); - uint8_t propertySize(PropertyID id); - uint8_t* data(uint32_t addr); - uint8_t getByte(uint32_t addr); - uint16_t getWord(uint32_t addr); - uint32_t getInt(uint32_t addr); - uint8_t* save(uint8_t* buffer); - uint8_t* restore(uint8_t* buffer); -private: - uint8_t _programVersion[5]; -}; \ No newline at end of file diff --git a/association_table_object.cpp b/association_table_object.cpp deleted file mode 100644 index 89eec6f..0000000 --- a/association_table_object.cpp +++ /dev/null @@ -1,69 +0,0 @@ -#include - -#include "association_table_object.h" -#include "bits.h" - -using namespace std; - - -AssociationTableObject::AssociationTableObject(uint8_t* memoryReference): TableObject(memoryReference) -{ - -} - -void AssociationTableObject::readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data) -{ - switch (id) - { - case PID_OBJECT_TYPE: - pushWord(OT_ASSOC_TABLE, data); - break; - default: - TableObject::readProperty(id, start, count, data); - } -} - -uint16_t AssociationTableObject::entryCount() -{ - return ntohs(_tableData[0]); -} - -uint16_t AssociationTableObject::operator[](uint16_t idx) -{ - if (idx < 0 || idx >= entryCount()) - return 0; - - return ntohs(_tableData[idx + 1]); -} - -uint8_t* AssociationTableObject::save(uint8_t* buffer) -{ - return TableObject::save(buffer); -} - -uint8_t* AssociationTableObject::restore(uint8_t* buffer) -{ - buffer = TableObject::restore(buffer); - _tableData = (uint16_t*)_data; - return buffer; -} - -int32_t AssociationTableObject::translateAsap(uint16_t asap) -{ - uint16_t entries = entryCount(); - for (uint16_t i = 0; i < entries; i++) - { - uint16_t entry = ntohs(operator[](i)); - if (lowByte(entry) == asap) - return highByte(entry); - } - return -1; -} - -void AssociationTableObject::beforeStateChange(LoadState& newState) -{ - if (newState != LS_LOADED) - return; - - _tableData = (uint16_t*)_data; -} \ No newline at end of file diff --git a/association_table_object.h b/association_table_object.h deleted file mode 100644 index ee4bd7d..0000000 --- a/association_table_object.h +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include "table_object.h" - -class AssociationTableObject: public TableObject -{ -public: - AssociationTableObject(uint8_t* memoryReference); - void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data); - uint16_t entryCount(); - uint16_t operator[](uint16_t idx); - uint8_t* save(uint8_t* buffer); - uint8_t* restore(uint8_t* buffer); - - int32_t translateAsap(uint16_t asap); -protected: - void beforeStateChange(LoadState& newState); -private: - uint16_t* _tableData; -}; \ No newline at end of file diff --git a/bau.cpp b/bau.cpp deleted file mode 100644 index 5a4d380..0000000 --- a/bau.cpp +++ /dev/null @@ -1,238 +0,0 @@ -#include "bau.h" - -void BusAccessUnit::groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, bool status) -{ -} - -void BusAccessUnit::groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType) -{ -} - -void BusAccessUnit::groupValueReadResponseConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopTtype, uint8_t * data, uint8_t dataLength, bool status) -{ -} - -void BusAccessUnit::groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength) -{ -} - -void BusAccessUnit::groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength, bool status) -{ -} - -void BusAccessUnit::groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength) -{ -} - -void BusAccessUnit::individualAddressWriteLocalConfirm(AckType ack, HopCountType hopType, uint16_t newaddress, bool status) -{ -} - -void BusAccessUnit::individualAddressWriteIndication(HopCountType hopType, uint16_t newaddress) -{ -} - -void BusAccessUnit::individualAddressReadLocalConfirm(AckType ack, HopCountType hopType, bool status) -{ -} - -void BusAccessUnit::individualAddressReadIndication(HopCountType hopType) -{ -} - -void BusAccessUnit::individualAddressReadResponseConfirm(AckType ack, HopCountType hopType, bool status) -{ -} - -void BusAccessUnit::individualAddressReadAppLayerConfirm(HopCountType hopType, uint16_t individualAddress) -{ -} - -void BusAccessUnit::individualAddressSerialNumberReadLocalConfirm(AckType ack, HopCountType hopType, uint8_t * serialNumber, bool status) -{ -} - -void BusAccessUnit::individualAddressSerialNumberReadIndication(HopCountType hopType, uint8_t * serialNumber) -{ -} - -void BusAccessUnit::individualAddressSerialNumberReadResponseConfirm(AckType ack, HopCountType hopType, uint8_t * serialNumber, uint16_t domainAddress, bool status) -{ -} - -void BusAccessUnit::individualAddressSerialNumberReadAppLayerConfirm(HopCountType hopType, uint8_t * serialNumber, uint16_t individualAddress, uint16_t domainAddress) -{ -} - -void BusAccessUnit::individualAddressSerialNumberWriteLocalConfirm(AckType ack, HopCountType hopType, uint8_t* serialNumber, uint16_t newaddress, bool status) -{ -} - -void BusAccessUnit::individualAddressSerialNumberWriteIndication(HopCountType hopType, uint8_t * serialNumber, uint16_t newaddress) -{ -} - -void BusAccessUnit::deviceDescriptorReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType, bool status) -{ -} - -void BusAccessUnit::deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType) -{ -} - -void BusAccessUnit::deviceDescriptorReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptor_type, - uint8_t * device_descriptor, bool status) -{ -} - -void BusAccessUnit::deviceDescriptorReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptortype, uint8_t * deviceDescriptor) -{ -} - -void BusAccessUnit::restartRequestLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, bool status) -{ -} - -void BusAccessUnit::restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap) -{ -} - -void BusAccessUnit::propertyValueReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, bool status) -{ -} - -void BusAccessUnit::propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex) -{ -} - -void BusAccessUnit::propertyValueReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, 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, 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, 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, uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t * data, uint8_t length) -{ -} - -void BusAccessUnit::propertyDescriptionReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool status) -{ -} - -void BusAccessUnit::propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex) -{ -} - -void BusAccessUnit::propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, 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, 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, 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, uint8_t number, uint16_t memoryAddress, bool status) -{ -} - -void BusAccessUnit::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress) -{ -} - -void BusAccessUnit::memoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, uint8_t * data, bool status) -{ -} - -void BusAccessUnit::memoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, uint8_t * data) -{ -} - -void BusAccessUnit::memoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, uint8_t * data, bool status) -{ -} - -void BusAccessUnit::memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress, uint8_t * data) -{ -} - -void BusAccessUnit::userMemoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, bool status) -{ -} - -void BusAccessUnit::userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress) -{ -} - -void BusAccessUnit::userMemoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t * memoryData, bool status) -{ -} - -void BusAccessUnit::userMemoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t * memoryData) -{ -} - -void BusAccessUnit::userMemoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t * memoryData, bool status) -{ -} - -void BusAccessUnit::userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t * memoryData) -{ -} - -void BusAccessUnit::userManufacturerInfoLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, bool status) -{ -} - -void BusAccessUnit::userManufacturerInfoIndication(Priority priority, HopCountType hopType, uint16_t asap) -{ -} - -void BusAccessUnit::userManufacturerInfoResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t * info, bool status) -{ -} - -void BusAccessUnit::userManufacturerInfoAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t * info) -{ -} - -void BusAccessUnit::authorizeLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint32_t key, bool status) -{ -} - -void BusAccessUnit::authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, uint32_t key) -{ -} - -void BusAccessUnit::authorizeResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, bool status) -{ -} - -void BusAccessUnit::authorizeAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level) -{ -} - -void BusAccessUnit::keyWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, uint32_t key, bool status) -{ -} - -void BusAccessUnit::keyWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, uint32_t key) -{ -} - -void BusAccessUnit::keyWriteResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, bool status) -{ -} - -void BusAccessUnit::keyWriteAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level) -{ -} diff --git a/bau.h b/bau.h deleted file mode 100644 index cb439a9..0000000 --- a/bau.h +++ /dev/null @@ -1,110 +0,0 @@ -#pragma once -#include -#include "knx_types.h" - -class BusAccessUnit -{ -public: - virtual void groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, bool status); - virtual void groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType); - virtual void groupValueReadResponseConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopTtype, - uint8_t* data, uint8_t dataLength, bool status); - virtual void groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, - uint8_t* data, uint8_t dataLength); - virtual void groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, - uint8_t* data, uint8_t dataLength, bool status); - virtual void groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, - uint8_t* data, uint8_t dataLength); - virtual void individualAddressWriteLocalConfirm(AckType ack, HopCountType hopType, - uint16_t newaddress, bool status); - virtual void individualAddressWriteIndication(HopCountType hopType, uint16_t newaddress); - virtual void individualAddressReadLocalConfirm(AckType ack, HopCountType hopType, bool status); - virtual void individualAddressReadIndication(HopCountType hopType); - virtual void individualAddressReadResponseConfirm(AckType ack, HopCountType hopType, bool status); - virtual void individualAddressReadAppLayerConfirm(HopCountType hopType, uint16_t individualAddress); - virtual void individualAddressSerialNumberReadLocalConfirm(AckType ack, HopCountType hopType, - uint8_t* serialNumber, bool status); - virtual void individualAddressSerialNumberReadIndication(HopCountType hopType, uint8_t* serialNumber); - virtual void individualAddressSerialNumberReadResponseConfirm(AckType ack, HopCountType hopType, - uint8_t* serialNumber, uint16_t domainAddress, bool status); - virtual void individualAddressSerialNumberReadAppLayerConfirm(HopCountType hopType, uint8_t* serialNumber, - uint16_t individualAddress, uint16_t domainAddress); - virtual void individualAddressSerialNumberWriteLocalConfirm(AckType ack, HopCountType hopType, uint8_t* serialNumber, - uint16_t newaddress, bool status); - virtual void individualAddressSerialNumberWriteIndication(HopCountType hopType, uint8_t* serialNumber, uint16_t newaddress); - virtual void deviceDescriptorReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t descriptorType, bool status); - virtual void deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType); - virtual void deviceDescriptorReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t descriptor_type, uint8_t* device_descriptor, bool status); - virtual void deviceDescriptorReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, - uint8_t descriptortype, uint8_t* deviceDescriptor); - virtual void restartRequestLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, bool status); - virtual void restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap); - virtual void propertyValueReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t objectIndex, uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, bool status); - virtual void propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, - uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex); - virtual void propertyValueReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, 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, 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, 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, uint8_t objectIndex, - uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length); - virtual void propertyDescriptionReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex, bool status); - virtual void propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, - uint8_t objectIndex, uint8_t propertyId, uint8_t propertyIndex); - virtual void propertyDescriptionReadResponse(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - 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, - 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, - 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, uint8_t number, - uint16_t memoryAddress, bool status); - virtual void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint16_t memoryAddress); - virtual void memoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint16_t memoryAddress, uint8_t* data, bool status); - virtual void memoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint16_t memoryAddress, uint8_t* data); - virtual void memoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint16_t memoryAddress, uint8_t* data, bool status); - virtual void memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint16_t memoryAddress, uint8_t* data); - virtual void userMemoryReadLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint32_t memoryAddress, bool status); - virtual void userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint32_t memoryAddress); - virtual void userMemoryReadResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint32_t memoryAddress, uint8_t* memoryData, bool status); - virtual void userMemoryReadAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint32_t memoryAddress, uint8_t* memoryData); - virtual void userMemoryWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint32_t memoryAddress, uint8_t* memoryData, bool status); - virtual void userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint32_t memoryAddress, uint8_t* memoryData); - virtual void userManufacturerInfoLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, bool status); - virtual void userManufacturerInfoIndication(Priority priority, HopCountType hopType, uint16_t asap); - virtual void userManufacturerInfoResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, - uint8_t* info, bool status); - virtual void userManufacturerInfoAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, - uint8_t* info); - virtual void authorizeLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint32_t key, bool status); - virtual void authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, uint32_t key); - virtual void authorizeResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, - bool status); - virtual void authorizeAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level); - virtual void keyWriteLocalConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, - uint32_t key, bool status); - virtual void keyWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, - uint32_t key); - virtual void keyWriteResponseConfirm(AckType ack, Priority priority, HopCountType hopType, uint16_t asap, uint8_t level, - bool status); - virtual void keyWriteAppLayerConfirm(Priority priority, HopCountType hopType, uint16_t asap, uint8_t level); -}; diff --git a/bau57B0.cpp b/bau57B0.cpp deleted file mode 100644 index d78e3b1..0000000 --- a/bau57B0.cpp +++ /dev/null @@ -1,286 +0,0 @@ -#include "bau57B0.h" -#include -#include - - -using namespace std; - -Bau57B0::Bau57B0(Platform& platform): _memoryReference((uint8_t*)&_deviceObj), _memory(platform), _addrTable(_memoryReference), - _assocTable(_memoryReference), _groupObjTable(_memoryReference), _appProgram(_memoryReference), - _ipParameters(_deviceObj, _platform), _platform(platform), _appLayer(_assocTable, *this), - _transLayer(_appLayer, _addrTable, _platform), _netLayer(_transLayer), - _dlLayer(_deviceObj, _addrTable, _ipParameters, _netLayer, _platform) -{ - _appLayer.transportLayer(_transLayer); - _transLayer.networkLayer(_netLayer); - _netLayer.dataLinkLayer(_dlLayer); - _memory.addSaveRestore(&_deviceObj); - _memory.addSaveRestore(&_ipParameters); - _memory.addSaveRestore(&_appProgram); - _memory.addSaveRestore(&_addrTable); - _memory.addSaveRestore(&_assocTable); - _memory.addSaveRestore(&_groupObjTable); -} - -void Bau57B0::loop() -{ - _dlLayer.loop(); - _transLayer.loop(); - sendNextGroupTelegram(); -} - -void Bau57B0::sendNextGroupTelegram() -{ - static uint16_t startIdx = 1; - - GroupObjectTableObject& table = _groupObjTable; - uint16_t objCount = table.entryCount(); - - for (uint16_t asap = startIdx; asap < objCount; asap++) - { - GroupObject& go = table.get(asap); - - ComFlag flag = go.commFlag(); - if (flag != ReadRequest && flag != WriteRequest) - continue; - - if(!go.communicationEnable() || ! go.transmitEnable()) - continue; - - if (flag == WriteRequest) - { - uint8_t* data = go.valueRef(); - _appLayer.groupValueWriteRequest(AckRequested, asap, go.priority(), NetworkLayerParameter, data, - go.sizeInTelegram()); - } - else - { - _appLayer.groupValueReadRequest(AckRequested, asap, go.priority(), NetworkLayerParameter); - } - - go.commFlag(Transmitting); - - startIdx = asap + 1; - return; - } - - startIdx = 1; -} - -void Bau57B0::updateGroupObject(GroupObject & go, uint8_t * data, uint8_t length) -{ - uint8_t* goData = go.valueRef(); - if (length != go.valueSize()) - { - go.commFlag(Error); - return; - } - - memcpy(goData, data, length); - - go.commFlag(cfUpdate); - if (go.updateHandler) - go.updateHandler(go); -} - -void Bau57B0::readMemory() -{ - _memory.readMemory(); -} - -DeviceObject& Bau57B0::deviceObject() -{ - return _deviceObj; -} - -GroupObjectTableObject& Bau57B0::groupObjectTable() -{ - return _groupObjTable; -} - -ApplicationProgramObject& Bau57B0::parameters() -{ - return _appProgram; -} - -bool Bau57B0::configured() -{ - return _groupObjTable.loadState() == LS_LOADED - && _addrTable.loadState() == LS_LOADED - && _assocTable.loadState() == LS_LOADED - && _appProgram.loadState() == LS_LOADED; -} - -bool Bau57B0::enabled() -{ - return _dlLayer.enabled(); -} - -void Bau57B0::enabled(bool value) -{ - _dlLayer.enabled(value); -} - -void Bau57B0::memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint16_t memoryAddress, uint8_t * data) -{ - memcpy(_memoryReference + memoryAddress, data, number); - _memory.memoryModified(); - - if (_deviceObj.verifyMode()) - memoryReadIndication(priority, hopType, asap, number, memoryAddress); -} - -void Bau57B0::memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint16_t memoryAddress) -{ - _appLayer.memoryReadResponse(AckRequested, priority, hopType, asap, number, memoryAddress, - _memoryReference + memoryAddress); -} - -void Bau57B0::deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType) -{ - if (descriptorType != 0) - descriptorType = 0x3f; - - uint8_t descriptor[] = { 0x57, 0xb0 }; - - _appLayer.deviceDescriptorReadResponse(AckRequested, priority, hopType, asap, descriptorType, descriptor); -} - -void Bau57B0::restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap) -{ - // for platforms that don't really restart - _deviceObj.progMode(false); - - // Flush the EEPROM before resetting - _memory.writeMemory(); - _platform.restart(); -} - -void Bau57B0::authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, uint32_t key) -{ - _appLayer.authorizeResponse(AckRequested, priority, hopType, asap, 0); -} - -void Bau57B0::userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress) -{ - _appLayer.userMemoryReadResponse(AckRequested, priority, hopType, asap, number, memoryAddress, - _memoryReference + memoryAddress); -} - -void Bau57B0::userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress, uint8_t* data) -{ - memcpy(_memoryReference + memoryAddress, data, number); - _memory.memoryModified(); - - if (_deviceObj.verifyMode()) - userMemoryReadIndication(priority, hopType, asap, number, memoryAddress); -} - -void Bau57B0::propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, - uint8_t propertyId, uint8_t propertyIndex) -{ - // TODO: reply correctly - _appLayer.propertyDescriptionReadResponse(AckRequested, priority, hopType, asap, objectIndex, propertyId, propertyIndex, - false, 0, 0, 0); -} - -void Bau57B0::propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, - uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length) -{ - InterfaceObject& obj = getInterfaceObject(objectIndex); - obj.writeProperty((PropertyID)propertyId, startIndex, data, numberOfElements); - propertyValueReadIndication(priority, hopType, asap, objectIndex, propertyId, numberOfElements, startIndex); -} - -void Bau57B0::propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, - uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex) -{ - InterfaceObject& obj = getInterfaceObject(objectIndex); - uint8_t elementSize = obj.propertySize((PropertyID)propertyId); - uint8_t size = elementSize * numberOfElements; - uint8_t data[size]; - obj.readProperty((PropertyID)propertyId, startIndex, numberOfElements, data); - _appLayer.propertyValueReadResponse(AckRequested, priority, hopType, asap, objectIndex, propertyId, numberOfElements, - startIndex, data, size); -} - -void Bau57B0::individualAddressReadIndication(HopCountType hopType) -{ - if (_deviceObj.progMode()) - _appLayer.individualAddressReadResponse(AckRequested, hopType); -} - -void Bau57B0::individualAddressWriteIndication(HopCountType hopType, uint16_t newaddress) -{ - if (_deviceObj.progMode()) - _deviceObj.induvidualAddress(newaddress); -} - -void Bau57B0::groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength, bool status) -{ - GroupObject& go = _groupObjTable.get(asap); - if (status) - go.commFlag(Ok); - else - go.commFlag(Error); -} - -void Bau57B0::groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, bool status) -{ - GroupObject& go = _groupObjTable.get(asap); - if (status) - go.commFlag(Ok); - else - go.commFlag(Error); -} - -void Bau57B0::groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType) -{ - GroupObject& go = _groupObjTable.get(asap); - uint8_t* data = go.valueRef(); - _appLayer.groupValueReadResponse(AckRequested, asap, priority, hopType, data, go.sizeInTelegram()); -} - -void Bau57B0::groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, uint8_t* data, - uint8_t dataLength) -{ - GroupObject& go = _groupObjTable.get(asap); - - if (!go.communicationEnable() || !go.responseUpdateEnable()) - return; - - updateGroupObject(go, data, dataLength); -} - -void Bau57B0::groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, uint8_t * data, uint8_t dataLength) -{ - GroupObject& go = _groupObjTable.get(asap); - - if (!go.communicationEnable() || !go.writeEnable()) - return; - - updateGroupObject(go, data, dataLength); -} - -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: - return _ipParameters; - default: - return _deviceObj; - } -} \ No newline at end of file diff --git a/bau57B0.h b/bau57B0.h deleted file mode 100644 index 5fd6718..0000000 --- a/bau57B0.h +++ /dev/null @@ -1,77 +0,0 @@ -#pragma once - -#include "bau.h" -#include "device_object.h" -#include "address_table_object.h" -#include "association_table_object.h" -#include "group_object_table_object.h" -#include "application_program_object.h" -#include "ip_parameter_object.h" -#include "application_layer.h" -#include "transport_layer.h" -#include "network_layer.h" -#include "data_link_layer.h" -#include "platform.h" -#include "memory.h" - -class Bau57B0: protected BusAccessUnit -{ - using BusAccessUnit::memoryReadIndication; -public: - Bau57B0(Platform& platform); - void loop(); - DeviceObject& deviceObject(); - GroupObjectTableObject& groupObjectTable(); - ApplicationProgramObject& parameters(); - bool configured(); - bool enabled(); - void enabled(bool value); - void readMemory(); -protected: - void memoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint16_t memoryAddress, uint8_t* data) override; - void memoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint16_t memoryAddress) override; - void deviceDescriptorReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t descriptorType); - void restartRequestIndication(Priority priority, HopCountType hopType, uint16_t asap); - void authorizeIndication(Priority priority, HopCountType hopType, uint16_t asap, uint32_t key); - void userMemoryReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, uint32_t memoryAddress); - void userMemoryWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t number, - uint32_t memoryAddress, uint8_t* memoryData); - void propertyDescriptionReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, - uint8_t propertyId, uint8_t propertyIndex); - void propertyValueWriteIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, - uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex, uint8_t* data, uint8_t length); - void propertyValueReadIndication(Priority priority, HopCountType hopType, uint16_t asap, uint8_t objectIndex, - uint8_t propertyId, uint8_t numberOfElements, uint16_t startIndex); - void individualAddressReadIndication(HopCountType hopType); - void individualAddressWriteIndication(HopCountType hopType, uint16_t newaddress); - void groupValueWriteLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, - uint8_t* data, uint8_t dataLength, bool status); - void groupValueReadLocalConfirm(AckType ack, uint16_t asap, Priority priority, HopCountType hopType, bool status); - void groupValueReadIndication(uint16_t asap, Priority priority, HopCountType hopType); - void groupValueReadAppLayerConfirm(uint16_t asap, Priority priority, HopCountType hopType, - uint8_t* data, uint8_t dataLength); - void groupValueWriteIndication(uint16_t asap, Priority priority, HopCountType hopType, - uint8_t* data, uint8_t dataLength); - - InterfaceObject& getInterfaceObject(uint8_t idx); - void sendNextGroupTelegram(); - void updateGroupObject(GroupObject& go, uint8_t* data, uint8_t length); -private: - DeviceObject _deviceObj; - // pointer to first private variable as reference to memory read/write commands - uint8_t* _memoryReference; - Memory _memory; - AddressTableObject _addrTable; - AssociationTableObject _assocTable; - GroupObjectTableObject _groupObjTable; - ApplicationProgramObject _appProgram; - IpParameterObject _ipParameters; - Platform& _platform; - ApplicationLayer _appLayer; - TransportLayer _transLayer; - NetworkLayer _netLayer; - DataLinkLayer _dlLayer; - -}; \ No newline at end of file diff --git a/bits.cpp b/bits.cpp deleted file mode 100644 index 380e5f9..0000000 --- a/bits.cpp +++ /dev/null @@ -1,75 +0,0 @@ -#include "bits.h" - -uint8_t* popByte(uint8_t& b, uint8_t* data) -{ - b = *data; - data += 1; - return data; -} - -uint8_t* popWord(uint16_t& w, uint8_t* data) -{ - w = getWord(data); - data += 2; - return data; -} - -uint8_t* popInt(uint32_t& i, uint8_t* data) -{ - i = getInt(data); - data += 4; - return data; -} - -uint8_t* popByteArray(uint8_t* dst, uint32_t size, 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(uint8_t* data) -{ - return (data[0] << 8) + data[1]; -} - -uint32_t getInt(uint8_t * data) -{ - return (data[0] << 24) + (data[1] << 16) + (data[2] << 8) + data[3]; -} diff --git a/bits.h b/bits.h deleted file mode 100644 index 9f8019c..0000000 --- a/bits.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include - -#ifdef __linux__ -#include -#define lowByte(val) ((val) & 255) -#define highByte(val) (((val) >> ((sizeof(val) - 1) << 3)) & 255) -#define bitRead(val, bitno) (((val) >> (bitno)) & 1) -#else -#include -#include -#define printf Serial.printf -#endif - - - -uint8_t* popByte(uint8_t& b, uint8_t* data); -uint8_t* popWord(uint16_t& w, uint8_t* data); -uint8_t* popInt(uint32_t& i, uint8_t* data); -uint8_t* popByteArray(uint8_t* dst, uint32_t size, 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(uint8_t* data); -uint32_t getInt(uint8_t* data); \ No newline at end of file diff --git a/cemi_frame.cpp b/cemi_frame.cpp deleted file mode 100644 index e50d90d..0000000 --- a/cemi_frame.cpp +++ /dev/null @@ -1,195 +0,0 @@ -#include "cemi_frame.h" -#include "bits.h" -#include "string.h" -#include - -CemiFrame::CemiFrame(uint8_t* data, uint16_t length): _npdu(data + NPDU_LPDU_DIFF, *this), - _tpdu(data + TPDU_LPDU_DIFF, *this), _apdu(data + APDU_LPDU_DIFF, *this) -{ - _data = data; - _ctrl1 = data + data[1] + 2; - _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 + 2; - _length = 0; - - memset(_data, 0, apduLength + APDU_LPDU_DIFF); - _ctrl1[0] |= Broadcast; - _npdu.octetCount(apduLength); -} - -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 + 2; - _length = other._length; - - memcpy(_data, other._data, other.totalLenght()); -} - -CemiFrame& CemiFrame::operator=(CemiFrame other) -{ - _length = other._length; - _data = buffer; - _ctrl1 = _data + 2; - 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 -{ - uint16_t tmp = - _npdu.length() + NPDU_LPDU_DIFF; - return tmp; -} - -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] & RepititionAllowed); -} - -void CemiFrame::repetition(Repetition rep) -{ - _ctrl1[0] &= ~RepititionAllowed; - _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; -} - -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); -} - -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[1 + _data[1] + NPDU_LPDU_DIFF]; - - if (_length != 0 && _length != (addInfoLen + apduLen + NPDU_LPDU_DIFF + 2)) - 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) - ) - return false; - - return true; -} diff --git a/cemi_frame.h b/cemi_frame.h deleted file mode 100644 index 2cb3807..0000000 --- a/cemi_frame.h +++ /dev/null @@ -1,60 +0,0 @@ -#pragma once - -#include "knx_types.h" -#include "stdint.h" -#include "npdu.h" -#include "tpdu.h" -#include "apdu.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) - -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; - - 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); - 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); - - NPDU& npdu(); - TPDU& tpdu(); - APDU& apdu(); - - bool valid() const; -private: - uint8_t buffer[0xff + NPDU_LPDU_DIFF]; //only valid of add info is zero - uint8_t* _data; - uint8_t* _ctrl1; - NPDU _npdu; - TPDU _tpdu; - APDU _apdu; - uint16_t _length; // only set if created from byte array -}; \ No newline at end of file diff --git a/data_link_layer.cpp b/data_link_layer.cpp deleted file mode 100644 index 61784bc..0000000 --- a/data_link_layer.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/* - * bus.cpp - Low level EIB bus access. - * - * Copyright (c) 2014 Stefan Taferner - * - * 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 "data_link_layer.h" - -#include "bits.h" -#include "platform.h" -#include "device_object.h" -#include "address_table_object.h" -#include "cemi_frame.h" - -#include -#include - -#define KNXIP_HEADER_LEN 0x6 -#define KNXIP_PROTOCOL_VERSION 0x10 - -#define ROUTING_INDICATION 0x0530 - -#define KNXIP_MULTICAST_PORT 3671 -#define MIN_LEN_CEMI 10 - -#ifdef DUMP_TELEGRAMS -unsigned char telBuffer[32]; -uint32_t telLength = 0; -#endif - -DataLinkLayer::DataLinkLayer(DeviceObject& devObj, AddressTableObject& addrTab, IpParameterObject& ipParam, - NetworkLayer& layer, Platform& platform) : - _deviceObject(devObj), _groupAddressTable(addrTab), _ipParameters(ipParam), _networkLayer(layer), _platform(platform) -{ -} - -void DataLinkLayer::dataRequest(AckType ack, AddressType addrType, uint16_t destinationAddr, FrameFormat format, Priority priority, NPDU& npdu) -{ - bool success = sendPacket(npdu, ack, destinationAddr, addrType, format, priority); - _networkLayer.dataConfirm(ack, addrType, destinationAddr, format, priority, npdu.frame().sourceAddress(), npdu, success); -} - -void DataLinkLayer::systemBroadcastRequest(AckType ack, FrameFormat format, Priority priority, NPDU& npdu) -{ - bool success = sendPacket(npdu, ack, 0, GroupAddress, format, priority); - _networkLayer.systemBroadcastConfirm(ack, format, priority, npdu.frame().sourceAddress(), npdu, success); -} - -bool DataLinkLayer::sendPacket(NPDU &npdu, AckType ack, uint16_t destinationAddr, AddressType addrType, FrameFormat format, Priority priority) -{ - CemiFrame& frame = npdu.frame(); - frame.messageCode(L_data_ind); - frame.destinationAddress(destinationAddr); - frame.sourceAddress(_deviceObject.induvidualAddress()); - frame.addressType(addrType); - frame.priority(priority); - frame.repetition(RepititionAllowed); - - if (npdu.octetCount() <= 15) - frame.frameType(StandardFrame); - else - frame.frameType(format); - - - if (!frame.valid()) - { - printf("invalid frame\n"); - return false; - } - - - //if (frame.npdu().octetCount() > 0) - //{ - // print.print("-> DLL "); - // frame.apdu().printPDU(); - //} - - - uint16_t length = frame.totalLenght() + KNXIP_HEADER_LEN; - uint8_t* buffer = new uint8_t[length]; - buffer[0] = KNXIP_HEADER_LEN; - buffer[1] = KNXIP_PROTOCOL_VERSION; - pushWord(ROUTING_INDICATION, buffer + 2); - pushWord(length, buffer + 4); - - memcpy(buffer + KNXIP_HEADER_LEN, frame._data, frame.totalLenght()); - - bool success = sendBytes(buffer, length); - // only send 50 packet per second: see KNX 3.2.6 p.6 - _platform.mdelay(20); - delete[] buffer; - return success; -} - -void DataLinkLayer::loop() -{ - if (!_enabled) - return; - - uint8_t buffer[512]; - int len = _platform.readBytes(buffer, 512); - if (len <= 0) - return; - - if (len < KNXIP_HEADER_LEN) - return; - - if (buffer[0] != KNXIP_HEADER_LEN - || buffer[1] != KNXIP_PROTOCOL_VERSION) - return; - - uint16_t code; - popWord(code, buffer + 2); - if (code != ROUTING_INDICATION) // only routing indication for now - return; - - if (len < MIN_LEN_CEMI) - return; - - //TODO: Check correct length (additions Info + apdu length) - CemiFrame frame(buffer + KNXIP_HEADER_LEN, len - KNXIP_HEADER_LEN); - 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.induvidualAddress(); - - if (source == ownAddr) - _deviceObject.induvidualAddressDuplication(true); - - if (addrType == GroupAddress && destination == 0) - _networkLayer.systemBroadcastIndication(ack, type, npdu, priority, source); - else - { - if (addrType == InduvidualAddress && destination != _deviceObject.induvidualAddress()) - return; - - if (addrType == GroupAddress && !_groupAddressTable.contains(destination)) - return; - - //if (frame.npdu().octetCount() > 0) - //{ - // print.print("<- DLL "); - // frame.apdu().printPDU(); - //} - - _networkLayer.dataIndication(ack, addrType, destination, type, npdu, priority, source); - } -} - -void DataLinkLayer::enabled(bool value) -{ - if (value && !_enabled) - { - _platform.setupMultiCast(_ipParameters.multicastAddress(), KNXIP_MULTICAST_PORT); - _enabled = true; - return; - } - - if(!value && _enabled) - { - _platform.closeMultiCast(); - _enabled = false; - return; - } -} - -bool DataLinkLayer::enabled() const -{ - return _enabled; -} - - -bool DataLinkLayer::sendBytes(uint8_t* bytes, uint16_t length) -{ - if (!_enabled) - return false; - -#ifdef DUMP_TELEGRAMS_ - { - print.print("QSD: "); - for (uint32_t i = 0; i <= length; ++i) - { - if (i) print.print(" "); - print.print(bytes[i], HEX, 2); - } - print.println(); - } -#endif - - return _platform.sendBytes(bytes, length); -} \ No newline at end of file diff --git a/data_link_layer.h b/data_link_layer.h deleted file mode 100644 index ce1ae3a..0000000 --- a/data_link_layer.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include -#include "device_object.h" -#include "address_table_object.h" -#include "ip_parameter_object.h" -#include "knx_types.h" -#include "network_layer.h" - -class DataLinkLayer -{ -public: - DataLinkLayer(DeviceObject& devObj, AddressTableObject& addrTab, IpParameterObject& ipParam, NetworkLayer& layer, - Platform& platform); - - // from network layer - void dataRequest(AckType ack, AddressType addrType, uint16_t destinationAddr, FrameFormat format, - Priority priority, NPDU& npdu); - void systemBroadcastRequest(AckType ack, FrameFormat format, Priority priority, NPDU& npdu); - void loop(); - void enabled(bool value); - bool enabled() const; -private: - bool _enabled = false; - bool sendPacket(NPDU &npdu, AckType ack, uint16_t destinationAddr, AddressType addrType, FrameFormat format, Priority priority); - bool sendBytes(uint8_t* buffer, uint16_t length); - - DeviceObject& _deviceObject; - AddressTableObject& _groupAddressTable; - IpParameterObject& _ipParameters; - NetworkLayer& _networkLayer; - Platform& _platform; -}; \ No newline at end of file diff --git a/datapoint_types.cpp b/datapoint_types.cpp deleted file mode 100644 index ef8b5fb..0000000 --- a/datapoint_types.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* - * datapoint_types.h - Conversion functions for datapoint types. - * - * Copyright (c) 2014 Stefan Taferner - * - * 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 - -// 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; -} diff --git a/datapoint_types.h b/datapoint_types.h deleted file mode 100644 index 1d4ff09..0000000 --- a/datapoint_types.h +++ /dev/null @@ -1,37 +0,0 @@ -/* - * datapoint_types.h - Conversion functions for datapoint types. - * - * Copyright (c) 2014 Stefan Taferner - * - * 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 - -/** - * 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); \ No newline at end of file diff --git a/device_object.cpp b/device_object.cpp deleted file mode 100644 index 3acb3bc..0000000 --- a/device_object.cpp +++ /dev/null @@ -1,253 +0,0 @@ -#include -#include "device_object.h" -#include "bits.h" - -void DeviceObject::readProperty(PropertyID propertyId, uint32_t start, uint32_t count, uint8_t* data) -{ - switch (propertyId) - { - case PID_OBJECT_TYPE: - pushWord(OT_DEVICE, data); - break; - case PID_SERIAL_NUMBER: - pushWord(_manufacturerId, data); - pushInt(_bauNumber, data); - break; - case PID_MANUFACTURER_ID: - pushWord(_manufacturerId, data); - break; - case PID_DEVICE_CONTROL: - *data = _deviceControl; - break; - case PID_ORDER_INFO: - pushByteArray((uint8_t*)_orderNumber, 10, data); - break; - case PID_HARDWARE_TYPE: - pushByteArray((uint8_t*)_hardwareType, 6, data); - break; - case PID_VERSION: - pushWord(_version, data); - break; - case PID_ROUTING_COUNT: - *data = _routingCount; - break; - case PID_PROG_MODE: - *data = _prgMode; - break; - case PID_MAX_APDU_LENGTH: - *data = 15; - break; - case PID_SUBNET_ADDR: - *data = ((_ownAddress >> 8) & 0xff); - break; - case PID_DEVICE_ADDR: - *data = (_ownAddress & 0xff); - break; - case PID_IO_LIST: - { - uint32_t ifObjs[] = { - 6, // length - OT_DEVICE, OT_ADDR_TABLE, OT_ASSOC_TABLE, OT_GRP_OBJ_TABLE, OT_APPLICATION_PROG, OT_IP_PARAMETER}; - - for (uint32_t i = start; i < (ifObjs[0] + 1) && i < count; i++) - pushInt(ifObjs[i], data); - - break; - } - case PID_DEVICE_DESCRIPTOR: - data[0] = 0x57; - data[1] = 0xB0; - break; - } -} - -void DeviceObject::writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count) -{ - switch (id) - { - case PID_DEVICE_CONTROL: - _deviceControl = data[0]; - break; - case PID_ROUTING_COUNT: - _routingCount = data[0]; - break; - case PID_PROG_MODE: - _prgMode = data[0]; - break; - } -} - -uint8_t DeviceObject::propertySize(PropertyID id) -{ - switch (id) - { - case PID_OBJECT_TYPE: - case PID_DEVICE_CONTROL: - case PID_ROUTING_COUNT: - case PID_PROG_MODE: - case PID_MAX_APDU_LENGTH: - case PID_SUBNET_ADDR: - case PID_DEVICE_ADDR: - return 1; - case PID_MANUFACTURER_ID: - case PID_VERSION: - case PID_DEVICE_DESCRIPTOR: - return 2; - case PID_IO_LIST: - return 4; - case PID_SERIAL_NUMBER: - case PID_HARDWARE_TYPE: - return 6; - case PID_ORDER_INFO: - return 10; - } - return 0; -} - -uint8_t* DeviceObject::save(uint8_t* buffer) -{ - buffer = pushByte(_deviceControl, buffer); - buffer = pushByte(_routingCount, buffer); - buffer = pushWord(_ownAddress, buffer); - return buffer; -} - -uint8_t* DeviceObject::restore(uint8_t* buffer) -{ - buffer = popByte(_deviceControl, buffer); - buffer = popByte(_routingCount, buffer); - buffer = popWord(_ownAddress, buffer); - _prgMode = 0; - return buffer; -} - -uint16_t DeviceObject::induvidualAddress() -{ - return _ownAddress; -} - -void DeviceObject::induvidualAddress(uint16_t value) -{ - _ownAddress = value; -} - -#define USER_STOPPED 0x1 -#define OWN_ADDR_DUPL 0x2 -#define VERIFY_MODE 0x4 -#define SAFE_STATE 0x8 - - -bool DeviceObject::userStopped() -{ - return (_deviceControl & USER_STOPPED) > 0; -} - -void DeviceObject::userStopped(bool value) -{ - if (value) - _deviceControl |= USER_STOPPED; - else - _deviceControl &= ~USER_STOPPED; -} - -bool DeviceObject::induvidualAddressDuplication() -{ - return (_deviceControl & OWN_ADDR_DUPL) > 0; -} - -void DeviceObject::induvidualAddressDuplication(bool value) -{ - if (value) - _deviceControl |= OWN_ADDR_DUPL; - else - _deviceControl &= ~OWN_ADDR_DUPL; -} - -bool DeviceObject::verifyMode() -{ - return (_deviceControl & VERIFY_MODE) > 0; -} - -void DeviceObject::verifyMode(bool value) -{ - if (value) - _deviceControl |= VERIFY_MODE; - else - _deviceControl &= ~VERIFY_MODE; -} - -bool DeviceObject::safeState() -{ - return (_deviceControl & SAFE_STATE) > 0; -} - -void DeviceObject::safeState(bool value) -{ - if (value) - _deviceControl |= SAFE_STATE; - else - _deviceControl &= ~SAFE_STATE; -} - -bool DeviceObject::progMode() -{ - return _prgMode == 1; -} - -void DeviceObject::progMode(bool value) -{ - if (value) - _prgMode = 1; - else - _prgMode = 0; -} - -uint16_t DeviceObject::manufacturerId() -{ - return _manufacturerId; -} - -void DeviceObject::manufacturerId(uint16_t value) -{ - _manufacturerId = value; -} - -uint32_t DeviceObject::bauNumber() -{ - return _bauNumber; -} - -void DeviceObject::bauNumber(uint32_t value) -{ - _bauNumber = value; -} - -const char* DeviceObject::orderNumber() -{ - return _orderNumber; -} - -void DeviceObject::orderNumber(const char* value) -{ - strncpy(_orderNumber, value, 10); -} - -const uint8_t* DeviceObject::hardwareType() -{ - return _hardwareType; -} - -void DeviceObject::hardwareType(const uint8_t* value) -{ - pushByteArray(value, 6, _hardwareType); -} - -uint16_t DeviceObject::version() -{ - return _version; -} - -void DeviceObject::version(uint16_t value) -{ - _version = value; -} diff --git a/device_object.h b/device_object.h deleted file mode 100644 index 6270bbd..0000000 --- a/device_object.h +++ /dev/null @@ -1,46 +0,0 @@ -#pragma once - -#include "interface_object.h" - -class DeviceObject: public InterfaceObject -{ -public: - void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data); - void writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count); - uint8_t propertySize(PropertyID id); - uint8_t* save(uint8_t* buffer); - uint8_t* restore(uint8_t* buffer); - - uint16_t induvidualAddress(); - void induvidualAddress(uint16_t value); - bool userStopped(); - void userStopped(bool value); - bool induvidualAddressDuplication(); - void induvidualAddressDuplication(bool value); - bool verifyMode(); - void verifyMode(bool value); - bool safeState(); - void safeState(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 char* orderNumber(); - void orderNumber(const char* value); - const uint8_t* hardwareType(); - void hardwareType(const uint8_t* value); - uint16_t version(); - void version(uint16_t value); -private: - uint8_t _deviceControl; - uint8_t _routingCount; - uint8_t _prgMode; - uint16_t _ownAddress; - uint16_t _manufacturerId; - uint32_t _bauNumber; - char _orderNumber[10]; - uint8_t _hardwareType[6]; - uint16_t _version; -}; \ No newline at end of file diff --git a/group_object.cpp b/group_object.cpp deleted file mode 100644 index d0e0307..0000000 --- a/group_object.cpp +++ /dev/null @@ -1,191 +0,0 @@ -#include "group_object.h" -#include "bits.h" -#include "string.h" -#include "datapoint_types.h" -#include "group_object_table_object.h" - -GroupObject::GroupObject(uint8_t size) -{ - _data = new uint8_t[size]; - _commFlag = Ok; - _table = 0; - _dataLength = size; - updateHandler = 0; -} - -GroupObject::~GroupObject() -{ - 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; - - 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) -{ - 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; -} - -int32_t GroupObject::objectReadFloat() -{ - uint16_t dptValue = getWord(_data); - return dptFromFloat(dptValue); -} - -bool GroupObject::objectReadBool() -{ - return _data[0] > 0; -} - -void GroupObject::requestObjectRead() -{ - _commFlag = ReadRequest; -} - -void GroupObject::objectWritten() -{ - _commFlag = WriteRequest; -} - - -void GroupObject::objectWriteFloat(int32_t value) -{ - uint16_t dptValue = dptToFloat(value); - pushWord(dptValue, _data); - objectWritten(); -} - - -void GroupObject::objectUpdateFloat(int32_t value) -{ - uint16_t dptValue = dptToFloat(value); - pushWord(dptValue, _data); - _commFlag = cfUpdate; -} - -size_t GroupObject::valueSize() -{ - return _dataLength; -} - -size_t GroupObject::sizeInTelegram() -{ - uint8_t code = lowByte(ntohs(_table->_tableData[_asap])); - return asapValueSize(code); -} diff --git a/group_object.h b/group_object.h deleted file mode 100644 index 1f35b75..0000000 --- a/group_object.h +++ /dev/null @@ -1,111 +0,0 @@ -#pragma once - -#include -#include -#include "knx_types.h" - -class GroupObjectTableObject; - -enum ComFlag -{ - cfUpdate = 0, - ReadRequest = 1, - WriteRequest = 2, - Transmitting = 3, - Ok = 4, - Error = 5 -}; - -class GroupObject; -typedef void (*GroupObjectUpdatedHandler)(GroupObject& go); - -class GroupObject -{ - friend class GroupObjectTableObject; -public: - GroupObject(uint8_t size); - virtual ~GroupObject(); - // config flags from ETS - bool responseUpdateEnable(); - bool transmitEnable(); - bool valueReadOnInit(); - bool writeEnable(); - bool readEnable(); - bool communicationEnable(); - Priority priority(); - - ComFlag commFlag(); - void commFlag(ComFlag value); - - /** - * Get the float value from a communication object. Can be used for - * communication objects of type 2 uint8_t float (EIS5 / DPT9). The value is in - * 1/100 - a DPT9 value of 21.01 is returned as 2101. - * - * @return The value of the com-object in 1/100. INVALID_DPT_FLOAT is returned - * for the DPT9 "invalid data" value. - */ - int32_t objectReadFloat(); - bool objectReadBool(); - /** - * 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. - * You can cycle through all updated communication objects with nextUpdatedObject(). - * - * - * @see objectWritten() - */ - void requestObjectRead(); - /** - * Mark a communication object as written. Use this function if you directly change - * the value of a communication object without using objectWrite(). Calling this - * function triggers the sending of a write-group-value telegram. - * - * @see requestObjectRead() - */ - void objectWritten(); - - /** - * Set the value of a communication object. Calling this function triggers the - * sending of a write-group-value telegram. - * - * The communication object is a 2 uint8_t float (EIS5 / DPT9) object. The value is - * in 1/100, so a value of 2101 would set a DPT9 float value of 21.01. The valid - * range of the values is -671088.64 to 670760.96. - * - * @param value - the new value of the communication object in 1/100. - * Use INVALID_DPT_FLOAT for the DPT9 "invalid data" value. - */ - void objectWriteFloat(int32_t value); - - /** - * Set the value of a communication object and mark the communication object - * as updated. This does not trigger a write-group-value telegram. - * - * The communication object is a 2 uint8_t float (EIS5 / DPT9) object. The value - * is in 1/100, so a value of 2101 would set a DPT9 float value of 21.01. - * The possible range of the values is -671088.64 to 670760.96. - * - * @param objno - the ID of the communication object. - * @param value - the new value of the communication object in 1/100. - * Use INVALID_DPT_FLOAT for the DPT9 "invalid data" value. - */ - void objectUpdateFloat(int32_t value); - - size_t valueSize(); - size_t asapValueSize(uint8_t code); - size_t sizeInTelegram(); - uint8_t* valueRef(); - uint16_t asap(); - GroupObjectUpdatedHandler updateHandler; -private: - size_t goSize(); - uint16_t _asap; - ComFlag _commFlag; - uint8_t* _data; - uint8_t _dataLength; - GroupObjectTableObject* _table; -}; diff --git a/group_object_table_object.cpp b/group_object_table_object.cpp deleted file mode 100644 index 7f3c71e..0000000 --- a/group_object_table_object.cpp +++ /dev/null @@ -1,121 +0,0 @@ -#include - -#include "group_object_table_object.h" -#include "group_object.h" -#include "bits.h" - -GroupObjectTableObject::GroupObjectTableObject(uint8_t* memoryReference): TableObject(memoryReference) -{ - _groupObjects = 0; - _groupObjectCount = 0; -} - -void GroupObjectTableObject::readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data) -{ - switch (id) - { - case PID_OBJECT_TYPE: - pushWord(OT_GRP_OBJ_TABLE, data); - break; - default: - TableObject::readProperty(id, start, count, data); - } -} - -uint16_t GroupObjectTableObject::entryCount() -{ - if (loadState() != LS_LOADED) - return 0; - - return ntohs(_tableData[0]); -} - - - -GroupObject& GroupObjectTableObject::get(uint16_t asap) -{ - return _groupObjects[asap - 1]; -} - - -uint8_t* GroupObjectTableObject::save(uint8_t* buffer) -{ - return TableObject::save(buffer); -} - - -uint8_t* GroupObjectTableObject::restore(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() == cfUpdate) - { - 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) -{ - _groupObjects = objs; - _groupObjectCount = size; - initGroupObjects(); -} - -void GroupObjectTableObject::beforeStateChange(LoadState& 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; - - uint16_t goCount = ntohs(_tableData[0]); - if (goCount != _groupObjectCount) - return false; - - for (uint16_t asap = 1; asap <= goCount; asap++) - { - GroupObject& go = _groupObjects[asap - 1]; - go._asap = asap; - go._table = this; - if (go._dataLength != go.goSize()) - return false; - } - - return true; -} diff --git a/group_object_table_object.h b/group_object_table_object.h deleted file mode 100644 index 4677ca1..0000000 --- a/group_object_table_object.h +++ /dev/null @@ -1,27 +0,0 @@ -#pragma once - -#include "table_object.h" -#include "group_object.h" - -class GroupObjectTableObject: public TableObject -{ - friend class GroupObject; - -public: - GroupObjectTableObject(uint8_t* memoryReference); - void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data); - uint16_t entryCount(); - GroupObject& get(uint16_t asap); - GroupObject& nextUpdatedObject(bool& valid); - void groupObjects(GroupObject* objs, uint16_t size); - - virtual uint8_t* save(uint8_t* buffer); - virtual uint8_t* restore(uint8_t* buffer); -protected: - virtual void beforeStateChange(LoadState& newState); -private: - bool initGroupObjects(); - uint16_t* _tableData = 0; - GroupObject* _groupObjects; - uint16_t _groupObjectCount; -}; \ No newline at end of file diff --git a/interface_object.h b/interface_object.h deleted file mode 100644 index d433a7b..0000000 --- a/interface_object.h +++ /dev/null @@ -1,62 +0,0 @@ -#pragma once - -#include -#include "property_types.h" -#include "save_restore.h" - -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 -}; - - -class InterfaceObject: public SaveRestore -{ -public: - virtual ~InterfaceObject() {} - virtual void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data) = 0; - virtual void writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count) = 0; - virtual uint8_t propertySize(PropertyID id) = 0; -protected: - -}; \ No newline at end of file diff --git a/ip_parameter_object.cpp b/ip_parameter_object.cpp deleted file mode 100644 index 52a7c5d..0000000 --- a/ip_parameter_object.cpp +++ /dev/null @@ -1,302 +0,0 @@ -#include "ip_parameter_object.h" -#include "device_object.h" -#include "platform.h" -#include "bits.h" - -//224.0.23.12 -#define DEFAULT_MULTICAST_ADDR 0xE000170C - -IpParameterObject::IpParameterObject(DeviceObject& deviceObject, Platform& platform): _deviceObject(deviceObject), - _platform(platform) -{} - -void IpParameterObject::readProperty(PropertyID propertyId, uint32_t start, uint32_t count, uint8_t* data) -{ - switch (propertyId) - { - case PID_LOAD_STATE_CONTROL: - data[0] = _state; - break; - case PID_OBJECT_TYPE: - pushWord(OT_IP_PARAMETER, data); - break; - case PID_PROJECT_INSTALLATION_ID: - pushWord(_projectInstallationId, data); - break; - case PID_KNX_INDIVIDUAL_ADDRESS: - pushWord(_deviceObject.induvidualAddress(), data); - break; - case PID_IP_ASSIGNMENT_METHOD: - data[0] = _ipAssignmentMethod; - break; - case PID_IP_CAPABILITIES: - data[0] = _ipCapabilities; - break; - case PID_CURRENT_IP_ADDRESS: - pushInt(_platform.currentIpAddress(), data); - break; - case PID_CURRENT_SUBNET_MASK: - pushInt(_platform.currentSubnetMask(), data); - break; - case PID_CURRENT_DEFAULT_GATEWAY: - pushInt(_platform.currentDefaultGateway(), data); - break; - case PID_IP_ADDRESS: - pushInt(_ipAddress, data); - break; - case PID_SUBNET_MASK: - pushInt(_subnetMask, data); - break; - case PID_DEFAULT_GATEWAY: - pushInt(_defaultGateway, data); - break; - case PID_MAC_ADDRESS: - { - uint8_t macAddr[6]; - _platform.macAddress(macAddr); - pushByteArray(macAddr, 6, data); - break; - } - case PID_SYSTEM_SETUP_MULTICAST_ADDRESS: - pushInt(DEFAULT_MULTICAST_ADDR, data); - break; - case PID_ROUTING_MULTICAST_ADDRESS: - pushInt(_multicastAddress, data); - break; - case PID_TTL: - data[0] = ttl(); - break; - case PID_KNXNETIP_DEVICE_CAPABILITIES: - data[0] = 0x1; - break; - case PID_FRIENDLY_NAME: - for (uint8_t i = start; i < start + count; i++) - data[i-start] = _friendlyName[i-1]; - break; - } -} - -void IpParameterObject::writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count) -{ - switch (id) - { - case PID_LOAD_STATE_CONTROL: - loadEvent(data); - break; - case PID_PROJECT_INSTALLATION_ID: - _projectInstallationId = getWord(data); - break; - case PID_KNX_INDIVIDUAL_ADDRESS: - _deviceObject.induvidualAddress(getWord(data)); - break; - case PID_IP_ASSIGNMENT_METHOD: - _ipAssignmentMethod = data[0]; - break; - case PID_IP_ADDRESS: - _ipAddress = getInt(data); - break; - case PID_SUBNET_MASK: - _subnetMask = getInt(data); - break; - case PID_DEFAULT_GATEWAY: - _defaultGateway = getInt(data); - break; - case PID_ROUTING_MULTICAST_ADDRESS: - _multicastAddress = getInt(data); - break; - case PID_TTL: - _ttl = data[0]; - break; - case PID_FRIENDLY_NAME: - for (uint8_t i = start; i < start + count; i++) - _friendlyName[i-1] = data[i - start]; - break; - } -} - -uint8_t IpParameterObject::propertySize(PropertyID id) -{ - switch (id) - { - case PID_IP_ASSIGNMENT_METHOD: - case PID_LOAD_STATE_CONTROL: - case PID_IP_CAPABILITIES: - case PID_TTL: - case PID_KNXNETIP_DEVICE_CAPABILITIES: - case PID_FRIENDLY_NAME: - return 1; - case PID_OBJECT_TYPE: - case PID_PROJECT_INSTALLATION_ID: - case PID_KNX_INDIVIDUAL_ADDRESS: - return 2; - case PID_CURRENT_IP_ADDRESS: - case PID_CURRENT_SUBNET_MASK: - case PID_CURRENT_DEFAULT_GATEWAY: - case PID_IP_ADDRESS: - case PID_SUBNET_MASK: - case PID_DEFAULT_GATEWAY: - case PID_SYSTEM_SETUP_MULTICAST_ADDRESS: - case PID_ROUTING_MULTICAST_ADDRESS: - return 4; - case PID_MAC_ADDRESS: - return 6; - } - return 0; -} - -uint8_t* IpParameterObject::save(uint8_t* buffer) -{ - buffer = pushWord(_projectInstallationId, buffer); - buffer = pushByte(_ipAssignmentMethod, buffer); - buffer = pushByte(_ipCapabilities, buffer); - buffer = pushInt(_ipAddress, buffer); - buffer = pushInt(_subnetMask, buffer); - buffer = pushInt(_defaultGateway, buffer); - buffer = pushInt(_multicastAddress, buffer); - buffer = pushByte(_ttl, buffer); - buffer = pushByteArray((uint8_t*)_friendlyName, 30, buffer); - - return buffer; -} - -uint8_t* IpParameterObject::restore(uint8_t* buffer) -{ - buffer = popWord(_projectInstallationId, buffer); - buffer = popByte(_ipAssignmentMethod, buffer); - buffer = popByte(_ipCapabilities, buffer); - buffer = popInt(_ipAddress, buffer); - buffer = popInt(_subnetMask, buffer); - buffer = popInt(_defaultGateway, buffer); - buffer = popInt(_multicastAddress, buffer); - buffer = popByte(_ttl, buffer); - buffer = popByteArray((uint8_t*)_friendlyName, 30, buffer); - - return buffer; -} - -uint32_t IpParameterObject::multicastAddress() const -{ - if (_multicastAddress == 0) - return DEFAULT_MULTICAST_ADDR; - - return _multicastAddress; -} - -void IpParameterObject::loadEvent(uint8_t* data) -{ - switch (_state) - { - case LS_UNLOADED: - loadEventUnloaded(data); - break; - case LS_LOADING: - loadEventLoading(data); - break; - case LS_LOADED: - loadEventLoaded(data); - break; - case LS_ERROR: - loadEventError(data); - break; - } -} - -void IpParameterObject::loadState(LoadState newState) -{ - if (newState == _state) - return; - //beforeStateChange(newState); - _state = newState; -} - -void IpParameterObject::loadEventUnloaded(uint8_t* data) -{ - uint8_t event = data[0]; - switch (event) - { - case LE_NOOP: - case LE_LOAD_COMPLETED: - case LE_ADDITIONAL_LOAD_CONTROLS: - case LE_UNLOAD: - break; - case LE_START_LOADING: - loadState(LS_LOADING); - break; - default: - loadState(LS_ERROR); - _errorCode = E_GOT_UNDEF_LOAD_CMD; - } -} - -void IpParameterObject::loadEventLoading(uint8_t* data) -{ - uint8_t event = data[0]; - switch (event) - { - case LE_NOOP: - case LE_START_LOADING: - break; - case LE_LOAD_COMPLETED: - loadState(LS_LOADED); - break; - case LE_UNLOAD: - loadState(LS_UNLOADED); - break; - case LE_ADDITIONAL_LOAD_CONTROLS: - additionalLoadControls(data); - break; - default: - loadState(LS_ERROR); - _errorCode = E_GOT_UNDEF_LOAD_CMD; - } -} - -void IpParameterObject::loadEventLoaded(uint8_t* data) -{ - uint8_t event = data[0]; - switch (event) - { - case LE_NOOP: - case LE_LOAD_COMPLETED: - break; - case LE_START_LOADING: - loadState(LS_LOADING); - break; - case LE_UNLOAD: - loadState(LS_UNLOADED); - break; - case LE_ADDITIONAL_LOAD_CONTROLS: - loadState(LS_ERROR); - _errorCode = E_INVALID_OPCODE; - break; - default: - loadState(LS_ERROR); - _errorCode = E_GOT_UNDEF_LOAD_CMD; - } -} - -void IpParameterObject::loadEventError(uint8_t* data) -{ - uint8_t event = data[0]; - switch (event) - { - case LE_NOOP: - case LE_LOAD_COMPLETED: - case LE_ADDITIONAL_LOAD_CONTROLS: - case LE_START_LOADING: - break; - case LE_UNLOAD: - loadState(LS_UNLOADED); - break; - default: - loadState(LS_ERROR); - _errorCode = E_GOT_UNDEF_LOAD_CMD; - } -} - -void IpParameterObject::additionalLoadControls(uint8_t* data) -{ - loadState(LS_ERROR); - _errorCode = E_INVALID_OPCODE; - return; -} \ No newline at end of file diff --git a/ip_parameter_object.h b/ip_parameter_object.h deleted file mode 100644 index 2b288d3..0000000 --- a/ip_parameter_object.h +++ /dev/null @@ -1,42 +0,0 @@ -#pragma once - -#include "interface_object.h" -#include "device_object.h" -#include "platform.h" - -class IpParameterObject: public InterfaceObject -{ -public: - IpParameterObject(DeviceObject& deviceObject, Platform& platform); - void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data); - void writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count); - uint8_t propertySize(PropertyID id); - - uint8_t* save(uint8_t* buffer); - uint8_t* restore(uint8_t* buffer); - - uint32_t multicastAddress() const; - uint8_t ttl() const { return _ttl; } -private: - uint16_t _projectInstallationId = 0; - uint8_t _ipAssignmentMethod = 0; - uint8_t _ipCapabilities = 0; - uint32_t _ipAddress = 0; - uint32_t _subnetMask = 0; - uint32_t _defaultGateway = 0; - uint32_t _multicastAddress = 0; - uint8_t _ttl = 60; - char _friendlyName[30] = { 0 }; - DeviceObject& _deviceObject; - Platform& _platform; - - void loadEvent(uint8_t* data); - void loadEventUnloaded(uint8_t* data); - void loadEventLoading(uint8_t* data); - void loadEventLoaded(uint8_t* data); - void loadEventError(uint8_t* data); - void additionalLoadControls(uint8_t* data); - void loadState(LoadState newState); - LoadState _state = LS_UNLOADED; - ErrorCode _errorCode = E_NO_FAULT; -}; \ No newline at end of file diff --git a/knx_types.h b/knx_types.h deleted file mode 100644 index 1c3cf81..0000000 --- a/knx_types.h +++ /dev/null @@ -1,103 +0,0 @@ -#pragma once - -enum FrameFormat -{ - ExtendedFrame = 0, - StandardFrame = 0x80 -}; - -enum Priority -{ - LowPriority = 0xC, - NormalPriority = 0x4, - UrgentPriority = 0x8, - SystemPriority = 0x0 -}; - -enum AckType -{ - AckDontCare = 0, - AckRequested = 0x2, -}; - -enum AddressType -{ - InduvidualAddress = 0, - GroupAddress = 0x80, -}; - -enum MessageCode -{ - L_data_ind = 0x29, -}; - -enum Repetition -{ - NoRepitiion = 0, - WasRepeated = 0, - RepititionAllowed = 0x20, - WasNotRepeated = 0x20, -}; - -enum SystemBroadcast -{ - SysBroadcast = 0, - Broadcast = 0x10, -}; - -enum Confirm -{ - ConfirmNoError = 0, - ConfirmError = 1, -}; - -enum HopCountType -{ - UnlimitedRouting, - NetworkLayerParameter -}; - -enum TpduType -{ - DataBroadcast, - DataGroup, - DataInduvidual, - DataConnected, - Connect, - Disconnect, - Ack, - Nack, -}; - -enum ApduType -{ - GroupValueRead = 0x000, - GroupValueResponse = 0x040, - GroupValueWrite = 0x080, - IndividualAddressWrite = 0x0c0, - IndividualAddressRead = 0x100, - IndividualAddressResponse = 0x140, - MemoryRead = 0x200, - MemoryResponse = 0x240, - MemoryWrite = 0x280, - UserMemoryRead = 0x2C0, - UserMemoryResponse = 0x2C1, - UserMemoryWrite = 0x2C2, - UserManufacturerInfoRead = 0x2C5, - UserManufacturerInfoResponse = 0x2C6, - DeviceDescriptorRead = 0x300, - DeviceDescriptorResponse = 0x340, - Restart = 0x380, - AuthorizeRequest = 0x3d1, - AuthorizeResponse = 0x3d2, - KeyWrite = 0x3d3, - KeyResponse = 0x3d4, - PropertyValueRead = 0x3d5, - PropertyValueResponse = 0x3d6, - PropertyValueWrite = 0x3d7, - PropertyDescriptionRead = 0x3d8, - PropertyDescriptionResponse = 0x3d9, - IndividualAddressSerialNumberRead = 0x3dc, - IndividualAddressSerialNumberResponse = 0x3dd, - IndividualAddressSerialNumberWrite = 0x3de, -}; \ No newline at end of file diff --git a/library.properties b/library.properties index 0adb20f..50c7755 100644 --- a/library.properties +++ b/library.properties @@ -1,9 +1,10 @@ name=knx_esp version=1.0.0 -author=tkunze -maintainer=tkunze -sentence=knx_esp Library +author=Thomas Kunze +maintainer=Thomas Kunze +sentence=knx stack for esp8266 paragraph= -category=Uncategorized -url=https://github/knx_esp -architectures=* \ No newline at end of file +category=Communication +url=https://github.com/thelsing/knx-esp +architectures=* +includes=knx_esp.h \ No newline at end of file diff --git a/memory.cpp b/memory.cpp deleted file mode 100644 index 702d192..0000000 --- a/memory.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "memory.h" - -Memory::Memory(Platform & platform): _platform(platform) -{ -} - -void Memory::memoryModified() -{ - _modified = true; -} - -bool Memory::isMemoryModified() -{ - return _modified; -} - -void Memory::readMemory() -{ - _data = _platform.getEepromBuffer(512); - - if (_data[0] != 0xDE || _data[1] != 0xAD || _data[2] != 0xAF || _data[3] != 0xFE) - return; - - uint8_t* buffer = _data + 4; - int size = _saveCount; - for (int i = 0; i < size; i++) - { - buffer = _saveRestores[i]->restore(buffer); - } -} - -void Memory::writeMemory() -{ - _data[0] = 0xDE; - _data[1] = 0xAD; - _data[2] = 0xAF; - _data[3] = 0xFE; - - uint8_t* buffer = _data + 4; - int size = _saveCount; - for (int i = 0; i < size; i++) - { - buffer = _saveRestores[i]->save(buffer); - } - _platform.commitToEeprom(); - _modified = false; -} - -void Memory::addSaveRestore(SaveRestore * obj) -{ - if (_saveCount >= MAXSAVE - 1) - return; - - _saveRestores[_saveCount] = obj; - _saveCount += 1; -} diff --git a/memory.h b/memory.h deleted file mode 100644 index a633802..0000000 --- a/memory.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include -#include "save_restore.h" -#include "platform.h" - -#define MAXSAVE 10 - -class Memory -{ -public: - Memory(Platform& platform); - void memoryModified(); - bool isMemoryModified(); - void readMemory(); - void writeMemory(); - void addSaveRestore(SaveRestore* obj); -private: - Platform& _platform; - bool _modified = false; - SaveRestore* _saveRestores[MAXSAVE]; - int _saveCount = 0; - uint8_t* _data = 0; -}; diff --git a/network_layer.cpp b/network_layer.cpp deleted file mode 100644 index be4b09c..0000000 --- a/network_layer.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include "network_layer.h" -#include "tpdu.h" -#include "cemi_frame.h" -#include "data_link_layer.h" -#include "bits.h" - -NetworkLayer::NetworkLayer(TransportLayer& layer): _transportLayer(layer) -{ - -} - -void NetworkLayer::dataLinkLayer(DataLinkLayer& layer) -{ - _dataLinkLayer = &layer; -} - -uint8_t NetworkLayer::hopCount() const -{ - return _hopCount; -} - -void NetworkLayer::hopCount(uint8_t value) -{ - _hopCount = value & 0x7; -} - -void NetworkLayer::dataIndication(AckType ack, AddressType addrType, uint16_t destination, FrameFormat format, NPDU& npdu, Priority priority, uint16_t source) -{ - HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter; - if (addrType == InduvidualAddress) - { - //if (npdu.octetCount() > 0) - //{ - // print.print("<- NL "); - // npdu.frame().apdu().printPDU(); - //} - _transportLayer.dataIndividualIndication(destination, hopType, priority, source, npdu.tpdu()); - return; - } - // group-address type - if (destination != 0) - { - _transportLayer.dataGroupIndication(destination, hopType, priority, source, npdu.tpdu()); - return; - } - // destination == 0 - _transportLayer.dataBroadcastIndication(hopType, priority, source, npdu.tpdu()); - -} - -void NetworkLayer::dataConfirm(AckType ack, AddressType addressType, uint16_t destination, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status) -{ - HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter; - if (addressType == InduvidualAddress) - { - _transportLayer.dataIndividualConfirm(ack, destination, hopType, priority, npdu.tpdu(), status); - return; - } - // group-address type - if (destination != 0) - { - _transportLayer.dataGroupConfirm(ack, source, destination, hopType, priority, npdu.tpdu(), status); - return; - } - // destination == 0 - _transportLayer.dataBroadcastConfirm(ack, hopType, priority, npdu.tpdu(), status); -} - -void NetworkLayer::systemBroadcastIndication(AckType ack, FrameFormat format, NPDU& npdu, Priority priority, uint16_t source) -{ - HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter; - _transportLayer.dataBroadcastIndication(hopType, priority, source, npdu.tpdu()); -} - -void NetworkLayer::systemBroadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status) -{ - HopCountType hopType = npdu.hopCount() == 7 ? UnlimitedRouting : NetworkLayerParameter; - _transportLayer.dataBroadcastConfirm(ack, hopType, priority, npdu.tpdu(), status); -} - -void NetworkLayer::dataIndividualRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) -{ - //if (tpdu.apdu().length() > 0) - //{ - // print.print("-> NL "); - // tpdu.apdu().printPDU(); - //} - sendDataRequest(tpdu, hopType, ack, destination, priority, InduvidualAddress); -} - -void NetworkLayer::sendDataRequest(TPDU &tpdu, HopCountType hopType, AckType ack, uint16_t destination, Priority priority, AddressType addrType) -{ - NPDU& npdu = tpdu.frame().npdu(); - - if (hopType == UnlimitedRouting) - npdu.hopCount(7); - else - npdu.hopCount(_hopCount); - - FrameFormat frameFormat = npdu.octetCount() > 15 ? ExtendedFrame : StandardFrame; - - _dataLinkLayer->dataRequest(ack, addrType, destination, frameFormat, priority, npdu); -} - -void NetworkLayer::dataGroupRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu) -{ - sendDataRequest(tpdu, hopType, ack, destination, priority, GroupAddress); -} - -void NetworkLayer::dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) -{ - sendDataRequest(tpdu, hopType, ack, 0, priority, GroupAddress); -} - -void NetworkLayer::dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu) -{ - NPDU& npdu = tpdu.frame().npdu(); - - if (hopType == UnlimitedRouting) - npdu.hopCount(7); - else - npdu.hopCount(_hopCount); - - FrameFormat frameFormat = npdu.octetCount() > 15 ? ExtendedFrame : StandardFrame; - - _dataLinkLayer->systemBroadcastRequest(ack, frameFormat, priority, npdu); -} diff --git a/network_layer.h b/network_layer.h deleted file mode 100644 index cc94b31..0000000 --- a/network_layer.h +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include "knx_types.h" -#include "npdu.h" -#include "transport_layer.h" -class DataLinkLayer; - -class NetworkLayer -{ -public: - NetworkLayer(TransportLayer& layer); - - void dataLinkLayer(DataLinkLayer& layer); - uint8_t hopCount() const; - void hopCount(uint8_t value); - - // from data link layer - void dataIndication(AckType ack, AddressType addType, uint16_t destination, FrameFormat format, NPDU& npdu, - Priority priority, uint16_t source); - void dataConfirm(AckType ack, AddressType addressType, uint16_t destination, FrameFormat format, Priority priority, - uint16_t source, NPDU& npdu, bool status); - void systemBroadcastIndication(AckType ack, FrameFormat format, NPDU& npdu, - Priority priority, uint16_t source); - void systemBroadcastConfirm(AckType ack, FrameFormat format, Priority priority, uint16_t source, NPDU& npdu, bool status); - - // from transport layer - void dataIndividualRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu); - void dataGroupRequest(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu); - void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu); - void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu); - -private: - void sendDataRequest(TPDU &tpdu, HopCountType hopType, AckType ack, uint16_t destination, Priority priority, AddressType addrType); - uint8_t _hopCount = 6; - DataLinkLayer* _dataLinkLayer; - TransportLayer& _transportLayer; -}; diff --git a/npdu.cpp b/npdu.cpp deleted file mode 100644 index a5b840c..0000000 --- a/npdu.cpp +++ /dev/null @@ -1,44 +0,0 @@ -#include "npdu.h" -#include "cemi_frame.h" -#include - - -NPDU::NPDU(uint8_t* data, CemiFrame& frame): _data(data), _frame(frame) -{ -} - - -uint8_t NPDU::octetCount() const -{ - return _data[0]; -} - -void NPDU::octetCount(uint8_t value) -{ - _data[0] = value; -} - -uint8_t NPDU::length() const -{ - return _data[0] + 2; // +1 for length field, +1 for TCPI -} - -uint8_t NPDU::hopCount() const -{ - return _frame.hopCount(); -} - -void NPDU::hopCount(uint8_t value) -{ - _frame.hopCount(value); -} - -CemiFrame& NPDU::frame() -{ - return _frame; -} - -TPDU& NPDU::tpdu() -{ - return _frame.tpdu(); -} diff --git a/npdu.h b/npdu.h deleted file mode 100644 index 3c4bf84..0000000 --- a/npdu.h +++ /dev/null @@ -1,24 +0,0 @@ -#pragma once - -#include - -class CemiFrame; -class TPDU; - -class NPDU -{ - friend class CemiFrame; -public: - NPDU(uint8_t* data, CemiFrame& frame); - - uint8_t octetCount() const; - void octetCount(uint8_t value); - uint8_t length() const; - uint8_t hopCount() const; - void hopCount(uint8_t value); - CemiFrame& frame(); - TPDU& tpdu(); -private: - uint8_t* _data; - CemiFrame& _frame; -}; \ No newline at end of file diff --git a/platform.h b/platform.h deleted file mode 100644 index 9efa224..0000000 --- a/platform.h +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#include -#include "save_restore.h" - -class Platform -{ -public: - virtual uint32_t currentIpAddress() = 0; - virtual uint32_t currentSubnetMask() = 0; - virtual uint32_t currentDefaultGateway() = 0; - virtual void macAddress(uint8_t* data) = 0; - - virtual uint32_t millis() = 0; - virtual void restart() = 0; - virtual void fatalError() = 0; - virtual void mdelay(uint32_t millis) = 0; - - virtual void setupMultiCast(uint32_t addr, uint16_t port) = 0; - virtual void closeMultiCast() = 0; - virtual bool sendBytes(uint8_t* buffer, uint16_t len) = 0; - virtual int readBytes(uint8_t* buffer, uint16_t maxLen) = 0; - - virtual uint8_t* getEepromBuffer(uint16_t size) = 0; - virtual void commitToEeprom() = 0; -}; \ No newline at end of file diff --git a/property_types.h b/property_types.h deleted file mode 100644 index 69c522b..0000000 --- a/property_types.h +++ /dev/null @@ -1,166 +0,0 @@ -/* - * property_types.h - BCU 2 property types of EIB objects. - * - * Copyright (c) 2014 Stefan Taferner - * - * 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 - -enum PropertyDataType -{ - PDT_CONTROL = 0x00, //!< length: 1 read, 10 write - PDT_CHAR = 0x01, //!< length: 1 - PDT_UNSIGNED_CHAR = 0x02, //!< length: 1 - PDT_INT = 0x03, //!< length: 2 - PDT_UNSIGNED_INT = 0x04, //!< length: 2 - PDT_KNX_FLOAT = 0x05, //!< length: 2 - PDT_DATE = 0x06, //!< length: 3 - PDT_TIME = 0x07, //!< length: 3 - PDT_LONG = 0x08, //!< length: 4 - PDT_UNSIGNED_LONG = 0x09, //!< length: 4 - PDT_FLOAT = 0x0a, //!< length: 4 - PDT_DOUBLE = 0x0b, //!< length: 8 - PDT_CHAR_BLOCK = 0x0c, //!< length: 10 - PDT_POLL_GROUP_SETTING = 0x0d, //!< length: 3 - PDT_SHORT_CHAR_BLOCK = 0x0e, //!< length: 5 - PDT_DATE_TIME = 0x0f, //!< length: 8 - PDT_VARIABLE_LENGTH = 0x10, - PDT_GENERIC_01 = 0x11, //!< length: 1 - PDT_GENERIC_02 = 0x12, //!< length: 2 - PDT_GENERIC_03 = 0x13, //!< length: 3 - PDT_GENERIC_04 = 0x14, //!< length: 4 - PDT_GENERIC_05 = 0x15, //!< length: 5 - PDT_GENERIC_06 = 0x16, //!< length: 6 - PDT_GENERIC_07 = 0x17, //!< length: 7 - PDT_GENERIC_08 = 0x18, //!< length: 8 - PDT_GENERIC_09 = 0x19, //!< length: 9 - PDT_GENERIC_10 = 0x1a, //!< length: 10 - PDT_GENERIC_11 = 0x1b, //!< length: 11 - PDT_GENERIC_12 = 0x1c, //!< length: 12 - PDT_GENERIC_13 = 0x1d, //!< length: 13 - PDT_GENERIC_14 = 0x1e, //!< length: 14 - PDT_GENERIC_15 = 0x1f, //!< length: 15 - PDT_GENERIC_16 = 0x20, //!< length: 16 - PDT_GENERIC_17 = 0x21, //!< length: 17 - PDT_GENERIC_18 = 0x22, //!< length: 18 - PDT_GENERIC_19 = 0x23, //!< length: 19 - PDT_GENERIC_20 = 0x24, //!< length: 20 - PDT_UTF8 = 0x2f, //!< length: 3 - PDT_VERSION = 0x30, //!< length: 3 - PDT_ALARM_INFO = 0x31, //!< length: 3 - PDT_BINARY_INFORMATION = 0x32, //!< length: 3 - PDT_BITSET8 = 0x33, //!< length: 3 - PDT_BITSET16 = 0x34, //!< length: 3 - PDT_ENUM8 = 0x35, //!< length: 3 - PDT_SCALING = 0x36, //!< length: 3 - PDT_NE_VL = 0x3c, //!< length: 3 - PDT_NE_FL = 0x3d, //!< length: 3 - PDT_FUNCTION = 0x3e, //!< length: 3 - PDT_ESCAPE = 0x3f, //!< length: 3 -}; - -enum PropertyID -{ - /** Interface Object Type independent Properties */ - PID_OBJECT_TYPE = 1, - PID_LOAD_STATE_CONTROL = 5, - PID_RUN_STATE_CONTROL = 6, - PID_TABLE_REFERENCE = 7, - PID_SERVICE_CONTROL = 8, - PID_FIRMWARE_REVISION = 9, - PID_SERIAL_NUMBER = 11, - PID_MANUFACTURER_ID = 12, - PID_PROG_VERSION = 13, - PID_DEVICE_CONTROL = 14, - PID_ORDER_INFO = 15, - PID_PEI_TYPE = 16, - PID_PORT_CONFIGURATION = 17, - PID_VERSION = 25, - PID_MCB_TABLE = 27, - PID_ERROR_CODE = 28, - - /** Properties in the Device Object */ - PID_ROUTING_COUNT = 51, - PID_PROG_MODE = 54, - PID_MAX_APDU_LENGTH = 56, - PID_SUBNET_ADDR = 57, - PID_DEVICE_ADDR = 58, - PID_IO_LIST = 71, - PID_HARDWARE_TYPE = 78, - PID_DEVICE_DESCRIPTOR = 83, - - /** KNXnet/IP Parameter Object */ - PID_PROJECT_INSTALLATION_ID = 51, - PID_KNX_INDIVIDUAL_ADDRESS = 52, - PID_ADDITIONAL_INDIVIDUAL_ADDRESSES = 53, - PID_CURRENT_IP_ASSIGNMENT_METHOD = 54, - PID_IP_ASSIGNMENT_METHOD = 55, - PID_IP_CAPABILITIES = 56, - PID_CURRENT_IP_ADDRESS = 57, - PID_CURRENT_SUBNET_MASK = 58, - PID_CURRENT_DEFAULT_GATEWAY = 59, - PID_IP_ADDRESS = 60, - PID_SUBNET_MASK = 61, - PID_DEFAULT_GATEWAY = 62, - PID_DHCP_BOOTP_SERVER = 63, - PID_MAC_ADDRESS = 64, - PID_SYSTEM_SETUP_MULTICAST_ADDRESS = 65, - PID_ROUTING_MULTICAST_ADDRESS = 66, - PID_TTL = 67, - PID_KNXNETIP_DEVICE_CAPABILITIES = 68, - PID_KNXNETIP_DEVICE_STATE = 69, - PID_KNXNETIP_ROUTING_CAPABILITIES = 70, - PID_PRIORITY_FIFO_ENABLED = 71, - PID_QUEUE_OVERFLOW_TO_IP = 72, - PID_QUEUE_OVERFLOW_TO_KNX = 73, - PID_MSG_TRANSMIT_TO_IP = 74, - PID_MSG_TRANSMIT_TO_KNX = 75, - PID_FRIENDLY_NAME = 76, - PID_ROUTING_BUSY_WAIT_TIME = 78, -}; - -enum LoadState -{ - LS_UNLOADED = 0, - LS_LOADED = 1, - LS_LOADING = 2, - LS_ERROR = 3, - LS_UNLOADING = 4, - LS_LOADCOMPLETING = 5 -}; - -enum LoadEvents -{ - LE_NOOP = 0, - LE_START_LOADING = 1, - LE_LOAD_COMPLETED = 2, - LE_ADDITIONAL_LOAD_CONTROLS = 3, - LE_UNLOAD = 4 -}; - -// 20.011 DPT_ErrorClass_System -enum ErrorCode -{ - E_NO_FAULT = 0, - E_GENERAL_DEVICE_FAULT = 1, - E_COMMUNICATION_FAULT = 2, - E_CONFIGURATION_FAULT = 3, - E_HARDWARE_FAULT = 4, - E_SOFTWARE_FAULT = 5, - E_INSUFFICIENT_NON_VOLATILE_MEMORY = 6, - E_INSUFFICIENT_VOLATILE_MEMORY = 7, - E_GOT_MEM_ALLOC_ZERO = 8, - E_CRC_ERROR = 9, - E_WATCHDOG_RESET = 10, - E_INVALID_OPCODE = 11, - E_GENERAL_PROTECTION_FAULT = 12, - E_MAX_TABLE_LENGTH_EXEEDED = 13, - E_GOT_UNDEF_LOAD_CMD = 14, - E_GAT_NOT_SORTED = 15, - E_INVALID_CONNECTION_NUMBER = 16, - E_INVALID_GO_NUMBER = 17, - E_GO_TYPE_TOO_BIG = 18 -}; \ No newline at end of file diff --git a/save_restore.h b/save_restore.h deleted file mode 100644 index 8a003db..0000000 --- a/save_restore.h +++ /dev/null @@ -1,10 +0,0 @@ -#pragma once -#include - - -class SaveRestore -{ -public: - virtual uint8_t* save(uint8_t* buffer) = 0; - virtual uint8_t* restore(uint8_t* buffer) = 0; -}; \ No newline at end of file diff --git a/esp_platform.cpp b/src/esp_platform.cpp similarity index 100% rename from esp_platform.cpp rename to src/esp_platform.cpp diff --git a/esp_platform.h b/src/esp_platform.h similarity index 93% rename from esp_platform.h rename to src/esp_platform.h index 1b113d0..9577d93 100644 --- a/esp_platform.h +++ b/src/esp_platform.h @@ -1,4 +1,4 @@ -#include "platform.h" +#include "knx/platform.h" #include #include diff --git a/knx_esp.h b/src/knx_esp.h similarity index 100% rename from knx_esp.h rename to src/knx_esp.h diff --git a/knx_facade.cpp b/src/knx_facade.cpp similarity index 100% rename from knx_facade.cpp rename to src/knx_facade.cpp diff --git a/knx_facade.h b/src/knx_facade.h similarity index 93% rename from knx_facade.h rename to src/knx_facade.h index f93a7c0..4d4ab69 100644 --- a/knx_facade.h +++ b/src/knx_facade.h @@ -1,7 +1,7 @@ #pragma once #include "esp_platform.h" -#include "bau57B0.h" +#include "knx/bau57B0.h" class KnxFacade { diff --git a/table_object.cpp b/table_object.cpp deleted file mode 100644 index 57fbb3f..0000000 --- a/table_object.cpp +++ /dev/null @@ -1,257 +0,0 @@ -#include -#include - -#include "table_object.h" -#include "bits.h" - -TableObject::TableObject(uint8_t* memoryReference): _memoryReference(memoryReference) -{ - -} - -void TableObject::readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data) -{ - switch (id) - { - case PID_LOAD_STATE_CONTROL: - data[0] = _state; - break; - case PID_TABLE_REFERENCE: - if (_state == LS_UNLOADED) - pushInt(0, data); - else - pushInt(tableReference(), data); - break; - case PID_ERROR_CODE: - data[0] = _errorCode; - break; - } -} - -void TableObject::writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count) -{ - switch (id) - { - case PID_LOAD_STATE_CONTROL: - loadEvent(data); - break; - - //case PID_MCB_TABLE: - // TODO - // break; - } -} - -uint8_t TableObject::propertySize(PropertyID id) -{ - switch (id) - { - case PID_LOAD_STATE_CONTROL: - return 1; - case PID_TABLE_REFERENCE: - return 4; - case PID_ERROR_CODE: - return 1; - } - return 0; -} - -TableObject::~TableObject() -{ - if (_data != 0) - free(_data); -} - -LoadState TableObject::loadState() -{ - return _state; -} - -void TableObject::loadState(LoadState newState) -{ - if (newState == _state) - return; - beforeStateChange(newState); - _state = newState; -} - - -uint8_t* TableObject::save(uint8_t* buffer) -{ - buffer = pushByte(_state, buffer); - buffer = pushByte(_errorCode, buffer); - buffer = pushInt(_size, buffer); - buffer = pushByteArray(_data, _size, buffer); - - return buffer; -} - - -uint8_t* TableObject::restore(uint8_t* buffer) -{ - uint8_t state = 0; - uint8_t errorCode = 0; - buffer = popByte(state, buffer); - buffer = popByte(errorCode, buffer); - _state = (LoadState)state; - _errorCode = (ErrorCode)errorCode; - - buffer = popInt(_size, buffer); - - if (_data) - free(_data); - - _data = (uint8_t*) malloc(_size); - - buffer = popByteArray(_data, _size, buffer); - - return buffer; -} - -uint32_t TableObject::tableReference() -{ - return (uint32_t)(_data - _memoryReference); -} - -bool TableObject::allocTable(uint32_t size, bool doFill, uint8_t fillByte) -{ - if (_data) - { - free(_data); - _size = 0; - } - - _data = (uint8_t*)malloc(size); - if (!_data) - return false; - - _size = size; - - if (doFill) - memset(_data, fillByte, size); - - return true; -} - -void TableObject::loadEvent(uint8_t* data) -{ - switch (_state) - { - case LS_UNLOADED: - loadEventUnloaded(data); - break; - case LS_LOADING: - loadEventLoading(data); - break; - case LS_LOADED: - loadEventLoaded(data); - break; - case LS_ERROR: - loadEventError(data); - break; - } -} - -void TableObject::loadEventUnloaded(uint8_t* data) -{ - uint8_t event = data[0]; - switch (event) - { - case LE_NOOP: - case LE_LOAD_COMPLETED: - case LE_ADDITIONAL_LOAD_CONTROLS: - case LE_UNLOAD: - break; - case LE_START_LOADING: - loadState(LS_LOADING); - break; - default: - loadState(LS_ERROR); - _errorCode = E_GOT_UNDEF_LOAD_CMD; - } -} - -void TableObject::loadEventLoading(uint8_t* data) -{ - uint8_t event = data[0]; - switch (event) - { - case LE_NOOP: - case LE_START_LOADING: - break; - case LE_LOAD_COMPLETED: - loadState(LS_LOADED); - break; - case LE_UNLOAD: - loadState(LS_UNLOADED); - break; - case LE_ADDITIONAL_LOAD_CONTROLS: - additionalLoadControls(data); - break; - default: - loadState(LS_ERROR); - _errorCode = E_GOT_UNDEF_LOAD_CMD; - } -} - -void TableObject::loadEventLoaded(uint8_t* data) -{ - uint8_t event = data[0]; - switch (event) - { - case LE_NOOP: - case LE_LOAD_COMPLETED: - break; - case LE_START_LOADING: - loadState(LS_LOADING); - break; - case LE_UNLOAD: - loadState(LS_UNLOADED); - break; - case LE_ADDITIONAL_LOAD_CONTROLS: - loadState(LS_ERROR); - _errorCode = E_INVALID_OPCODE; - break; - default: - loadState(LS_ERROR); - _errorCode = E_GOT_UNDEF_LOAD_CMD; - } -} - -void TableObject::loadEventError(uint8_t* data) -{ - uint8_t event = data[0]; - switch (event) - { - case LE_NOOP: - case LE_LOAD_COMPLETED: - case LE_ADDITIONAL_LOAD_CONTROLS: - case LE_START_LOADING: - break; - case LE_UNLOAD: - loadState(LS_UNLOADED); - break; - default: - loadState(LS_ERROR); - _errorCode = E_GOT_UNDEF_LOAD_CMD; - } -} - -void TableObject::additionalLoadControls(uint8_t* data) -{ - if (data[1] != 0x0B) // Data Relative Allocation - { - loadState(LS_ERROR); - _errorCode = E_INVALID_OPCODE; - return; - } - - size_t size = ((data[2] << 24) | (data[3] << 16) | (data[4] << 8) | data[5]); - bool doFill = data[6] == 0x1; - uint8_t fillByte = data[7]; - if (!allocTable(size, doFill, fillByte)) - { - loadState(LS_ERROR); - _errorCode = E_MAX_TABLE_LENGTH_EXEEDED; - } -} \ No newline at end of file diff --git a/table_object.h b/table_object.h deleted file mode 100644 index b5f23af..0000000 --- a/table_object.h +++ /dev/null @@ -1,33 +0,0 @@ -#pragma once - -#include "interface_object.h" - -class TableObject: public InterfaceObject -{ -public: - TableObject(uint8_t* memoryReference); - virtual void readProperty(PropertyID id, uint32_t start, uint32_t count, uint8_t* data); - virtual void writeProperty(PropertyID id, uint8_t start, uint8_t* data, uint8_t count); - virtual uint8_t propertySize(PropertyID id); - virtual ~TableObject(); - LoadState loadState(); - virtual uint8_t* save(uint8_t* buffer); - virtual uint8_t* restore(uint8_t* buffer); -protected: - virtual void beforeStateChange(LoadState& newState) {} - uint8_t* _data = 0; - uint32_t _size = 0; - ErrorCode _errorCode = E_NO_FAULT; -private: - uint32_t tableReference(); - bool allocTable(uint32_t size, bool doFill, uint8_t fillByte); - void loadEvent(uint8_t* data); - void loadEventUnloaded(uint8_t* data); - void loadEventLoading(uint8_t* data); - void loadEventLoaded(uint8_t* data); - void loadEventError(uint8_t* data); - void additionalLoadControls(uint8_t* data); - void loadState(LoadState newState); - LoadState _state = LS_UNLOADED; - uint8_t* _memoryReference; -}; diff --git a/tpdu.cpp b/tpdu.cpp deleted file mode 100644 index ff4c6ee..0000000 --- a/tpdu.cpp +++ /dev/null @@ -1,127 +0,0 @@ -#include "tpdu.h" -#include "cemi_frame.h" - -TPDU::TPDU(uint8_t* data, CemiFrame& frame): _data(data), _frame(frame) -{ -} - -TpduType TPDU::type() const -{ - if (control()) - { - if (numbered()) - { - if ((_data[0] & 1) == 0) - return Ack; - else - return Nack; - } - else if ((_data[0] & 1) == 0) - return Connect; - else - return Disconnect; - } - else - { - if (_frame.addressType() == GroupAddress) - { - if (_frame.destinationAddress() == 0) - return DataBroadcast; - else - return DataGroup; - } - else if (numbered()) - return DataConnected; - else - return DataInduvidual; - } -} - -void TPDU::type(TpduType type) -{ - switch (type) - { - case DataBroadcast: - case DataGroup: - case DataInduvidual: - _data[0] &= 0x3; - break; - case DataConnected: - _data[0] &= 0xC3; - _data[0] |= 0x40; - break; - case Connect: - _data[0] = 0x80; - break; - case Disconnect: - _data[0] = 0x81; - break; - case Ack: - _data[0] &= ~0xC3; - _data[0] |= 0xC2; - break; - case Nack: - _data[0] |= 0xC3; - break; - } -} - -bool TPDU::numbered() const -{ - return (_data[0] & 0x40) > 0; -} - -void TPDU::numbered(bool value) -{ - if (value) - _data[0] |= 0x40; - else - _data[0] &= ~0x40; -} - -bool TPDU::control() const -{ - return (_data[0] & 0x80) > 0; -} - -void TPDU::control(bool value) -{ - if (value) - _data[0] |= 0x80; - else - _data[0] &= ~0x80; -} - -uint8_t TPDU::sequenceNumber() const -{ - return ((_data[0] >> 2) & 0xF); -} - -void TPDU::sequenceNumber(uint8_t value) -{ - _data[0] &= ~(0xF << 2); - _data[0] |= ((value & 0xF) << 2); -} - -APDU& TPDU::apdu() -{ - return _frame.apdu(); -} - -CemiFrame& TPDU::frame() -{ - return _frame; -} - -void TPDU::printPDU() -{ -/* print.print("TPDU: "); - print.print(type(), HEX, 2); - print.print(" "); - for (uint8_t i = 0; i < apdu().length() + 1; ++i) - { - if (i) print.print(" "); - print.print(_data[i], HEX, 2); - } - print.println()*/; -} \ No newline at end of file diff --git a/tpdu.h b/tpdu.h deleted file mode 100644 index b981443..0000000 --- a/tpdu.h +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "stdint.h" -#include "knx_types.h" -class CemiFrame; -class APDU; - -class TPDU -{ - friend class CemiFrame; -public: - TPDU(uint8_t* data, CemiFrame& frame); - TpduType type() const; - void type(TpduType type); - - bool numbered() const; - void numbered(bool value); - - bool control() const; - void control(bool value); - - uint8_t sequenceNumber() const; - void sequenceNumber(uint8_t value); - - APDU& apdu(); - - CemiFrame& frame(); - void printPDU(); -private: - uint8_t* _data; - CemiFrame& _frame; -}; diff --git a/transport_layer.cpp b/transport_layer.cpp deleted file mode 100644 index 92de287..0000000 --- a/transport_layer.cpp +++ /dev/null @@ -1,711 +0,0 @@ -#include "transport_layer.h" -#include "apdu.h" -#include "cemi_frame.h" -#include "network_layer.h" -#include "application_layer.h" -#include "platform.h" -#include - -TransportLayer::TransportLayer(ApplicationLayer& layer, AddressTableObject& gat, Platform& platform): _savedFrame(0), - _savedFrameConnecting(0), _applicationLayer(layer), _groupAddressTable(gat), _platform(platform) -{ - _currentState = Closed; -} - -void TransportLayer::networkLayer(NetworkLayer& layer) -{ - _networkLayer = &layer; -} - -void TransportLayer::dataIndividualIndication(uint16_t destination, HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu) -{ - //if (tpdu.apdu().length() > 0) - //{ - // print.print("<- TL "); - // tpdu.printPDU(); - // print.print("<- TL "); - // tpdu.apdu().printPDU(); - //} - - uint8_t sequenceNo = tpdu.sequenceNumber(); - switch (tpdu.type()) - { - case DataInduvidual: - _applicationLayer.dataIndividualIndication(hopType, priority, source, tpdu.apdu()); - return; - case DataConnected: - if (source == _connectionAddress) - { - if (sequenceNo == _seqNoRecv) - { - //E4 - switch (_currentState) - { - case Closed: - //A0 nothing - break; - case OpenIdle: - case OpenWait: - A2(source, priority, tpdu.apdu()); - break; - case Connecting: - _currentState = Closed; - A6(destination); - break; - } - } - else if(sequenceNo == ((_seqNoRecv -1) & 0xF)) - { - //E5 - switch (_currentState) - { - case Closed: - //A0 - break; - case OpenIdle: - case OpenWait: - case Connecting: - A3(source, priority, tpdu); - break; - } - } - else - { - //E6 - switch (_currentState) - { - case Closed: - //A0 - break; - case OpenIdle: - case OpenWait: - A4(source, priority, tpdu); - break; - case Connecting: - A6(destination); - break; - } - } - } - else - { - //E7 - switch (_currentState) - { - case Closed: - case OpenIdle: - case OpenWait: - //A0 - break; - case Connecting: - A10(source); - break; - } - } - break; - case Connect: - if (source == _connectionAddress) - { - //E0 - switch (_currentState) - { - case Closed: - _currentState = OpenIdle; - A1(source); - break; - case OpenWait: - case OpenIdle: - case Connecting: - //A0: do nothing - break; - } - } - else - { - //E1 - switch (_currentState) - { - case Closed: - _currentState = OpenIdle; - A1(source); - break; - case OpenIdle: - case OpenWait: - case Connecting: - A10(source); - break; - } - } - break; - case Disconnect: - if (source == _connectionAddress) - { - //E2 - switch (_currentState) - { - case Closed: - //A0 do nothing - break; - case OpenIdle: - case OpenWait: - case Connecting: - _currentState = Closed; - A5(source); - break; - default: - break; - } - } - else - { - //E3 - //A0: do nothing - } - break; - case Ack: - if (source == _connectionAddress) - { - if (sequenceNo == _seqNoSend) - { - //E8 - switch (_currentState) - { - case Closed: - case OpenIdle: - //A0 - break; - case OpenWait: - _currentState = OpenIdle; - A8(); - break; - case Connecting: - _currentState = Closed; - A6(source); - break; - } - } - else - { - //E9 - switch (_currentState) - { - case Closed: - case OpenIdle: - //A0 - break; - case OpenWait: - case Connecting: - _currentState = Closed; - A6(source); - break; - } - } - } - else - { - //E10 - switch (_currentState) - { - case Connecting: - A10(source); - break; - } - } - break; - case Nack: - if (source == _connectionAddress) - { - if (sequenceNo != _seqNoSend) - { - //E11 - switch (_currentState) - { - case Closed: - case OpenIdle: - case OpenWait: - //A0 - break; - case Connecting: - _currentState = Closed; - A6(source); - break; - } - } - else - { - if (_repCount < _maxRepCount) - { - //E12 - switch (_currentState) - { - case Closed: - //A0 - break; - case Connecting: - case OpenIdle: - _currentState = Closed; - A6(source); - break; - case OpenWait: - A9(); - break; - } - } - else - { - //E13 - switch (_currentState) - { - case Closed: - //A0 - break; - case OpenIdle: - case OpenWait: - case Connecting: - _currentState = Closed; - A6(source); - break; - } - } - } - } - else - { - //E14 - switch (_currentState) - { - case Closed: - case OpenIdle: - case OpenWait: - //A0 - break; - case Connecting: - A10(source); - break; - default: - break; - } - } - break; - default: - break; - } -} - -void TransportLayer::dataIndividualConfirm(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu, bool status) -{ - TpduType type = tpdu.type(); - switch (type) - { - case DataInduvidual: - _applicationLayer.dataIndividualConfirm(ack, hopType, priority, destination, tpdu.apdu(), status); - break; - case DataConnected: - //E22 - //A0: do nothing - break; - case Connect: - if (status) - { - //E19 - switch (_currentState) - { - case Closed: - case OpenIdle: - case OpenWait: - //A0: do nothing - break; - case Connecting: - _currentState = OpenIdle; - A13(destination); - break; - } - } - else - { - //E20 - switch (_currentState) - { - case Closed: - case OpenIdle: - case OpenWait: - //A0: do nothing - break; - case Connecting: - A5(destination); - break; - } - - } - break; - case Disconnect: - //E21 - //A0: do nothing - break; - case Ack: - //E23 - //A0: do nothing - break; - case Nack: - //E24 - //A0: do nothing - break; - } -} - -void TransportLayer::dataGroupIndication(uint16_t destination, HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu) -{ - uint16_t tsap = _groupAddressTable.getTsap(destination); - _applicationLayer.dataGroupIndication(hopType, priority, tsap, tpdu.apdu()); -} - -void TransportLayer::dataGroupConfirm(AckType ack, uint16_t source, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu, bool status) -{ - _applicationLayer.dataGroupConfirm(ack, hopType, priority, destination, tpdu.apdu(), status); -} - -void TransportLayer::dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu) -{ - _applicationLayer.dataBroadcastIndication(hopType, priority, source, tpdu.apdu()); -} - -void TransportLayer::dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu, bool status) -{ - _applicationLayer.dataBroadcastConfirm(ack, hopType, priority, tpdu.apdu(), status); -} - -void TransportLayer::dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu) -{ - _applicationLayer.dataSystemBroadcastIndication(hopType, priority, source, tpdu.apdu()); -} - -void TransportLayer::dataSystemBroadcastConfirm(AckType ack, HopCountType hopType, TPDU& tpdu, Priority priority, bool status) -{ - _applicationLayer.dataSystemBroadcastConfirm(hopType, priority, tpdu.apdu(), status); -} - -void TransportLayer::dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu) -{ - uint16_t groupAdress = _groupAddressTable.getGa(tsap); - TPDU& tpdu = apdu.frame().tpdu(); - _networkLayer->dataGroupRequest(ack, groupAdress, hopType, priority, tpdu); -} - -void TransportLayer::dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu) -{ - TPDU& tpdu = apdu.frame().tpdu(); - _networkLayer->dataBroadcastRequest(ack, hopType, priority, tpdu); -} - -void TransportLayer::dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu) -{ - TPDU& tpdu = apdu.frame().tpdu(); - return _networkLayer->dataSystemBroadcastRequest(ack, hopType, priority, tpdu); -} - -void TransportLayer::dataIndividualRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t destination, APDU& apdu) -{ - //print.print("-> TL "); - //apdu.printPDU(); - TPDU& tpdu = apdu.frame().tpdu(); - _networkLayer->dataIndividualRequest(ack, destination, hopType, priority, tpdu); -} - -void TransportLayer::connectRequest(uint16_t destination, Priority priority) -{ - //E25 - switch (_currentState) - { - case Closed: - _currentState = Connecting; - A12(destination, priority); - break; - case OpenIdle: - case OpenWait: - case Connecting: - _currentState = Closed; - A6(destination); - break; - } -} - -void TransportLayer::disconnectRequest(uint16_t tsap, Priority priority) -{ - //E26 - switch (_currentState) - { - case Closed: - A15(priority, tsap); - break; - case OpenIdle: - case OpenWait: - case Connecting: - _currentState = Closed; - A14(tsap, priority); - break; - } -} - -void TransportLayer::dataConnectedRequest(uint16_t tsap, Priority priority, APDU& apdu) -{ - //print.print("-> TL "); - //apdu.printPDU(); - //E15 - switch (_currentState) - { - case Closed: - //A0 - break; - case OpenIdle: - _currentState = OpenWait; - A7(priority, apdu); - break; - case OpenWait: - case Connecting: - A11(tsap, priority, apdu); - break; - default: - break; - } -} - -void TransportLayer::connectionTimeoutIndication() -{ - //E16 - switch (_currentState) - { - case Closed: - //A0: do nothing - break; - case OpenIdle: - case OpenWait: - case Connecting: - _currentState = Closed; - A6(_connectionAddress); - break; - } -} - -void TransportLayer::ackTimeoutIndication() -{ - if (_repCount < _maxRepCount) - { - //E17 - switch (_currentState) - { - case Closed: - case OpenIdle: - case Connecting: - //A0: do nothing - break; - case OpenWait: - A9(); - break; - } - } - else - { - //E18 - switch (_currentState) - { - case Closed: - case OpenIdle: - case Connecting: - //A0: do nothing - break; - case OpenWait: - _currentState = Closed; - A6(_connectionAddress); - break; - } - } -} - - - -void TransportLayer::loop() -{ - uint32_t millis = _platform.millis(); - if (_connectionTimeoutEnabled - && (millis - _connectionTimeoutStartMillis) > _connectionTimeoutMillis) - connectionTimeoutIndication(); - - if (_ackTimeoutEnabled - && (millis - _ackTimeoutStartMillis) > _ackTimeoutMillis) - ackTimeoutIndication(); - - if (_savedConnectingValid) - { - //retry saved event - _savedConnectingValid = false; - dataConnectedRequest(_savedTsapConnecting, _savedPriorityConnecting, _savedFrameConnecting.apdu()); - } -} - -void TransportLayer::sendControlTelegram(TpduType pduType, uint8_t seqNo) -{ - CemiFrame frame(0); - TPDU& tpdu = frame.tpdu(); - tpdu.type(pduType); - tpdu.sequenceNumber(seqNo); - _networkLayer->dataIndividualRequest(AckRequested, _connectionAddress, NetworkLayerParameter, - SystemPriority, tpdu); -} - -void TransportLayer::A0() -{ - /* do nothing */ -} - -void TransportLayer::A1(uint16_t source) -{ - _connectionAddress = source; - _applicationLayer.connectIndication(source); - _seqNoSend = 0; - _seqNoRecv = 0; -} - -void incSeqNr(uint8_t& seqNr) -{ - seqNr += 1; - if (seqNr > 0xf) - seqNr = 0; -} - -void TransportLayer::A2(uint16_t source, Priority priority, APDU& apdu) -{ - sendControlTelegram(Ack, _seqNoRecv); - incSeqNr(_seqNoRecv); - _applicationLayer.dataConnectedIndication(priority, source, apdu); - enableConnectionTimeout(); -} - -void TransportLayer::A3(uint16_t source, Priority priority, TPDU& recTpdu) -{ - sendControlTelegram(Ack, recTpdu.sequenceNumber()); - enableConnectionTimeout(); -} - -void TransportLayer::A4(uint16_t source, Priority priority, TPDU& recTpdu) -{ - sendControlTelegram(Nack, recTpdu.sequenceNumber()); - enableConnectionTimeout(); -} - -void TransportLayer::A5(uint16_t tsap) -{ - _applicationLayer.disconnectIndication(tsap); - disableConnectionTimeout(); - disableAckTimeout(); -} - -void TransportLayer::A6(uint16_t tsap) -{ - sendControlTelegram(Disconnect, 0); - _applicationLayer.disconnectIndication(tsap); - disableConnectionTimeout(); - disableAckTimeout(); -} - -void TransportLayer::A7(Priority priority, APDU& apdu) -{ - _savedPriority = priority; - _savedFrame = apdu.frame(); - TPDU& tpdu = apdu.frame().tpdu(); - tpdu.type(DataConnected); - tpdu.sequenceNumber(_seqNoSend); - _networkLayer->dataIndividualRequest(AckRequested, _connectionAddress, NetworkLayerParameter, priority, tpdu); - _repCount = 0; - enableAckTimeout(); - enableConnectionTimeout(); -} - -void TransportLayer::A8() -{ - disableAckTimeout(); - incSeqNr(_seqNoSend); - _applicationLayer.dataConnectedConfirm(0); - enableConnectionTimeout(); -} - -void TransportLayer::A9() -{ - TPDU& tpdu = _savedFrame.tpdu(); - // tpdu is still initialized from last send - _networkLayer->dataIndividualRequest(AckRequested, _connectionAddress, NetworkLayerParameter, _savedPriority, tpdu); - _repCount += 1; - enableAckTimeout(); - enableConnectionTimeout(); -} - -void TransportLayer::A10(uint16_t source) -{ - CemiFrame frame(0); - TPDU& tpdu = frame.tpdu(); - tpdu.type(Disconnect); - tpdu.sequenceNumber(0); - _networkLayer->dataIndividualRequest(AckRequested, source, NetworkLayerParameter, SystemPriority, tpdu); -} - -void TransportLayer::A11(uint16_t tsap, Priority priority, APDU& apdu) -{ - _savedTsapConnecting = tsap; - _savedPriorityConnecting = priority; - _savedFrameConnecting = apdu.frame(); - _savedConnectingValid = true; -} - -void TransportLayer::A12(uint16_t destination, Priority priority) -{ - _connectionAddress = destination; - CemiFrame frame(0); - TPDU& tpdu = frame.tpdu(); - tpdu.type(Connect); - _seqNoRecv = 0; - _seqNoSend = 0; - enableConnectionTimeout(); -} - -void TransportLayer::A13(uint16_t destination) -{ - _applicationLayer.connectConfirm(destination, 0, true); -} - -void TransportLayer::A14(uint16_t tsap, Priority priority) -{ - CemiFrame frame(0); - TPDU& tpdu = frame.tpdu(); - tpdu.type(Disconnect); - tpdu.sequenceNumber(0); - _networkLayer->dataIndividualRequest(AckRequested, _connectionAddress, NetworkLayerParameter, SystemPriority, tpdu); - _applicationLayer.disconnectConfirm(priority, tsap, true); -} - -void TransportLayer::A15(Priority priority, uint16_t tsap) -{ - _applicationLayer.disconnectConfirm(priority, tsap, true); - disableConnectionTimeout(); - disableAckTimeout(); -} - -void TransportLayer::enableConnectionTimeout() -{ - _connectionTimeoutStartMillis = _platform.millis(); - _connectionTimeoutEnabled = true; -} - -void TransportLayer::disableConnectionTimeout() -{ - _connectionTimeoutEnabled = false; -} - -void TransportLayer::enableAckTimeout() -{ - _ackTimeoutStartMillis = _platform.millis(); - _ackTimeoutEnabled = true; -} - -void TransportLayer::disableAckTimeout() -{ - _ackTimeoutEnabled = false; -} diff --git a/transport_layer.h b/transport_layer.h deleted file mode 100644 index 5d883f4..0000000 --- a/transport_layer.h +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once - -#include -#include "knx_types.h" -#include "tpdu.h" -#include "address_table_object.h" -#include "cemi_frame.h" - -class ApplicationLayer; -class APDU; -class NetworkLayer; -class Platform; - -enum StateType { Closed, OpenIdle, OpenWait, Connecting }; - -class TransportLayer -{ -public: - TransportLayer(ApplicationLayer& layer, AddressTableObject& gat, Platform& platform); - void networkLayer(NetworkLayer& layer); - - // from network layer - void dataIndividualIndication(uint16_t destination, HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu); - void dataIndividualConfirm(AckType ack, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu, bool status); - void dataGroupIndication(uint16_t destination, HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu); - void dataGroupConfirm(AckType ack, uint16_t source, uint16_t destination, HopCountType hopType, Priority priority, TPDU& tpdu, bool status); - void dataBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu); - void dataBroadcastConfirm(AckType ack, HopCountType hopType, Priority priority, TPDU& tpdu, bool status); - void dataSystemBroadcastIndication(HopCountType hopType, Priority priority, uint16_t source, TPDU& tpdu); - void dataSystemBroadcastConfirm(AckType ack, HopCountType hopType, TPDU& tpdu, Priority priority, bool status); - - // fromp application layer - void dataGroupRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t tsap, APDU& apdu); - void dataBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu); - void dataSystemBroadcastRequest(AckType ack, HopCountType hopType, Priority priority, APDU& apdu); - void dataIndividualRequest(AckType ack, HopCountType hopType, Priority priority, uint16_t destination, APDU& apdu); - - void connectRequest(uint16_t destination, Priority priority); - void disconnectRequest(uint16_t tsap, Priority priority); - // apdu must be valid until it was confirmed - void dataConnectedRequest(uint16_t tsap, Priority priority, APDU& apdu); - - // other - void connectionTimeoutIndication(); - void ackTimeoutIndication(); - void loop(); -private: -#pragma region States - Priority _savedPriority = LowPriority; - CemiFrame _savedFrame; - Priority _savedPriorityConnecting; - CemiFrame _savedFrameConnecting; - uint16_t _savedTsapConnecting; - bool _savedConnectingValid = false; - enum StateEvent - { - E0, E1, E2, E3, E4, E5, E6, E7, E8, E9, E10, E11, E12, E13, E14, - E15, E16, E17, E18, E19, E20, E21, E22, E23, E24, E25, E26, E27 - }; - StateType _currentState = Closed; - void sendControlTelegram(TpduType pduType, uint8_t seqNo); - void A0(); - void A1(uint16_t source); - void A2(uint16_t source, Priority priority, APDU& apdu); - void A3(uint16_t source, Priority priority, TPDU& recTpdu); - void A4(uint16_t source, Priority priority, TPDU& recTpdu); - void A5(uint16_t source); - void A6(uint16_t source); - void A7(Priority priority, APDU& apdu); - void A8(); - void A9(); - void A10(uint16_t source); - void A11(uint16_t tsap, Priority priority, APDU& apdu); - void A12(uint16_t destination, Priority priority); - void A13(uint16_t destination); - void A14(uint16_t destination, Priority priority); - void A15(Priority priority, uint16_t tsap); - void enableConnectionTimeout(); - void disableConnectionTimeout(); - void enableAckTimeout(); - void disableAckTimeout(); - uint16_t _connectionAddress = 0; - uint8_t _seqNoSend = 0; - uint8_t _seqNoRecv = 0; - bool _connectionTimeoutEnabled = false; - uint32_t _connectionTimeoutStartMillis = 0; - uint16_t _connectionTimeoutMillis = 6000; - bool _ackTimeoutEnabled = false; - uint32_t _ackTimeoutStartMillis = 0; - uint16_t _ackTimeoutMillis = 3000; - uint8_t _repCount = 0; - uint8_t _maxRepCount = 3; -#pragma endregion - ApplicationLayer& _applicationLayer; - AddressTableObject& _groupAddressTable; - NetworkLayer* _networkLayer; - Platform& _platform; -}; From 809de53cc8037659b3c332079469be9e2243d469 Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Mon, 9 Apr 2018 23:58:35 +0200 Subject: [PATCH 10/24] move more code from demo to knx-esp --- examples/knx-esp-demo.ino | 88 ++++++++++ examples/knx-esp-demo.xml | 133 ++++++++++++++ knx-esp.vcxitems | 119 +++++++------ knx-esp.vcxitems.filters | 324 ++++++++++++++++++++--------------- src/button.cpp | 25 +++ src/button.h | 4 + src/knx_facade.cpp | 42 +++++ src/knx_facade.h | 13 +- src/led.cpp | 28 +++ src/led.h | 3 + src/nowifistate.cpp | 35 ++++ src/nowifistate.h | 16 ++ src/programmingmodestate.cpp | 26 +++ src/programmingmodestate.h | 17 ++ src/runningstate.cpp | 40 +++++ src/runningstate.h | 20 +++ src/state.cpp | 53 ++++++ src/state.h | 29 ++++ src/wpsstate.cpp | 28 +++ src/wpsstate.h | 14 ++ 20 files changed, 865 insertions(+), 192 deletions(-) create mode 100644 examples/knx-esp-demo.ino create mode 100644 examples/knx-esp-demo.xml create mode 100644 src/button.cpp create mode 100644 src/button.h create mode 100644 src/led.cpp create mode 100644 src/led.h create mode 100644 src/nowifistate.cpp create mode 100644 src/nowifistate.h create mode 100644 src/programmingmodestate.cpp create mode 100644 src/programmingmodestate.h create mode 100644 src/runningstate.cpp create mode 100644 src/runningstate.h create mode 100644 src/state.cpp create mode 100644 src/state.h create mode 100644 src/wpsstate.cpp create mode 100644 src/wpsstate.h diff --git a/examples/knx-esp-demo.ino b/examples/knx-esp-demo.ino new file mode 100644 index 0000000..ce98407 --- /dev/null +++ b/examples/knx-esp-demo.ino @@ -0,0 +1,88 @@ +#include +#include "knx_esp.h" + +float currentValue = 0; +float maxValue = 0; +float minValue = RAND_MAX; +long lastsend = 0; + +GroupObject groupObjects[] +{ + GroupObject(2), + GroupObject(2), + GroupObject(2), + GroupObject(1) +}; + +GroupObject& goCurrent = groupObjects[0]; +GroupObject& goMax = groupObjects[1]; +GroupObject& goMin = groupObjects[2]; +GroupObject& goReset = groupObjects[3]; + +void measureTemp() +{ + long now = millis(); + if ((now - lastsend) < 2000) + return; + + lastsend = now; + int r = rand(); + currentValue = (r * 1.0) / (RAND_MAX * 1.0); + currentValue *= 100 * 100; + + goCurrent.objectWriteFloat(currentValue); + + if (currentValue > maxValue) + { + maxValue = currentValue; + goMax.objectWriteFloat(maxValue); + } + + if (currentValue < minValue) + { + minValue = currentValue; + goMin.objectWriteFloat(minValue); + } +} + +void resetCallback(GroupObject& go) +{ + if (go.objectReadBool()) + { + maxValue = 0; + minValue = 10000; + } +} + +void setup() +{ + Serial.begin(115200); + Serial.setDebugOutput(true); + + randomSeed(millis()); + + knx.registerGroupObjects(groupObjects, 4); + knx.readMemory(); + + goReset.updateHandler = resetCallback; + + if (knx.configured()) + { + Serial.printf("Timeout: %d\n", knx.paramByte(0)); + Serial.printf("Zykl. senden: %d\n", knx.paramByte(1)); + Serial.printf("Min/Max senden: %d\n", knx.paramByte(2)); + Serial.printf("Aenderung senden: %d\n", knx.paramByte(3)); + Serial.printf("Abgleich %d\n", knx.paramByte(4)); + } + + knx.start(); +} + +void loop() +{ + knx.loop(); + if (!knx.configured()) + return; + + measureTemp(); +} diff --git a/examples/knx-esp-demo.xml b/examples/knx-esp-demo.xml new file mode 100644 index 0000000..da93378 --- /dev/null +++ b/examples/knx-esp-demo.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/knx-esp.vcxitems b/knx-esp.vcxitems index caaa10d..2b91085 100644 --- a/knx-esp.vcxitems +++ b/knx-esp.vcxitems @@ -15,62 +15,75 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/knx-esp.vcxitems.filters b/knx-esp.vcxitems.filters index 3cf624e..e304b22 100644 --- a/knx-esp.vcxitems.filters +++ b/knx-esp.vcxitems.filters @@ -9,168 +9,216 @@ {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hh;hpp;hxx - - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - + + {0ee56052-c43f-46b9-ab25-f805ed81e840} + + + {e0ae5c46-92be-4bc5-bcae-4e831818010c} + - - Header Files - - + Header Files - + Header Files - + Header Files - + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + + Header Files\knx + + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - + Header Files - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - + Header Files + + + Source Files + + + Source Files + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files\knx + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + \ No newline at end of file diff --git a/src/button.cpp b/src/button.cpp new file mode 100644 index 0000000..e5a719b --- /dev/null +++ b/src/button.cpp @@ -0,0 +1,25 @@ +#include "button.h" +#include "state.h" +#include "knx_facade.h" + +unsigned long buttonTimestamp = 0; +void buttonUp() +{ + if (millis() - buttonTimestamp > 1000) + { + Serial.println("long button press"); + currentState->longButtonPress(); + } + else + { + Serial.println("short button press"); + currentState->shortButtonPress(); + } + attachInterrupt(knx.buttonPin(), buttonDown, FALLING); +} + +void buttonDown() +{ + buttonTimestamp = millis(); + attachInterrupt(knx.buttonPin(), buttonUp, RISING); +} \ No newline at end of file diff --git a/src/button.h b/src/button.h new file mode 100644 index 0000000..1c32762 --- /dev/null +++ b/src/button.h @@ -0,0 +1,4 @@ +#pragma once + +void buttonDown(); +void buttonUp(); \ No newline at end of file diff --git a/src/knx_facade.cpp b/src/knx_facade.cpp index a7c7e88..89a6e7a 100644 --- a/src/knx_facade.cpp +++ b/src/knx_facade.cpp @@ -1,4 +1,8 @@ #include "knx_facade.h" +#include "state.h" +#include "button.h" +#include "led.h" +#include "nowifistate.h" KnxFacade knx; @@ -32,12 +36,38 @@ bool KnxFacade::configured() return _bau.configured(); } +uint32_t KnxFacade::ledPin() +{ + return _ledPin; +} + +void KnxFacade::ledPin(uint32_t value) +{ + _ledPin = value; +} + +uint32_t KnxFacade::buttonPin() +{ + return _buttonPin; +} + +void KnxFacade::buttonPin(uint32_t value) +{ + _buttonPin = value; +} + void KnxFacade::readMemory() { _bau.readMemory(); } void KnxFacade::loop() +{ + if (currentState) + currentState->loop(); +} + +void KnxFacade::knxLoop() { _bau.loop(); } @@ -72,6 +102,18 @@ void KnxFacade::version(uint16_t value) _bau.deviceObject().version(value); } +void KnxFacade::start() +{ + pinMode(_ledPin, OUTPUT); + + pinMode(_buttonPin, INPUT); + attachInterrupt(_buttonPin, buttonDown, FALLING); + + switchToSate(noWifiState); + checkStates(); + _ticker.attach_ms(100, doLed); +} + uint8_t* KnxFacade::paramData(uint32_t addr) { if (!_bau.configured()) diff --git a/src/knx_facade.h b/src/knx_facade.h index 4d4ab69..ce90334 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -1,8 +1,10 @@ #pragma once - +#include #include "esp_platform.h" #include "knx/bau57B0.h" +class RunningState; + class KnxFacade { public: @@ -12,14 +14,20 @@ public: bool progMode(); void progMode(bool value); bool configured(); + uint32_t ledPin(); + void ledPin(uint32_t value); + uint32_t buttonPin(); + void buttonPin(uint32_t value); void readMemory(); void loop(); + void knxLoop(); void registerGroupObjects(GroupObject* groupObjects, uint16_t count); void manufacturerId(uint16_t value); void bauNumber(uint32_t value); void orderNumber(const char* value); void hardwareType(uint8_t* value); void version(uint16_t value); + void start(); uint8_t* paramData(uint32_t addr); uint8_t paramByte(uint32_t addr); uint16_t paramWord(uint32_t addr); @@ -27,6 +35,9 @@ public: private: EspPlatform _platform; Bau57B0 _bau; + uint32_t _ledPin = 16; + uint32_t _buttonPin = 0; + Ticker _ticker; }; extern KnxFacade knx; \ No newline at end of file diff --git a/src/led.cpp b/src/led.cpp new file mode 100644 index 0000000..e1a66a6 --- /dev/null +++ b/src/led.cpp @@ -0,0 +1,28 @@ +#include "led.h" +#include "knx_facade.h" +#include "state.h" + +void doLed() +{ + if (!currentState) + return; + + if (!currentState->ledOn()) + { + digitalWrite(knx.ledPin(), HIGH); + return; + } + + unsigned int period = currentState->blinkPeriod(); + + if (!currentState->ledBlink() || period == 0) + { + digitalWrite(knx.ledPin(), LOW); + return; + } + + if ((millis() % period) > (period / 2)) + digitalWrite(knx.ledPin(), HIGH); + else + digitalWrite(knx.ledPin(), LOW); +} \ No newline at end of file diff --git a/src/led.h b/src/led.h new file mode 100644 index 0000000..985759a --- /dev/null +++ b/src/led.h @@ -0,0 +1,3 @@ +#pragma once + +void doLed(); \ No newline at end of file diff --git a/src/nowifistate.cpp b/src/nowifistate.cpp new file mode 100644 index 0000000..ddb6073 --- /dev/null +++ b/src/nowifistate.cpp @@ -0,0 +1,35 @@ +#include + +#include "nowifistate.h" +#include "wpsstate.h" +#include "runningstate.h" + +NoWifiState noWifiState = NoWifiState(); + +void NoWifiState::shortButtonPress() +{ + switchToSate(wpsState); +} + +void NoWifiState::longButtonPress() +{ + switchToSate(wpsState); +} + +void NoWifiState::enterState() +{ + WiFi.mode(WIFI_STA); + WiFi.begin("", ""); + while (WiFi.status() == WL_DISCONNECTED) + { + delay(500); + Serial.print("."); + } + + wl_status_t status = WiFi.status(); + if (status == WL_CONNECTED) + { + Serial.printf("\nConnected successful to SSID '%s'\n", WiFi.SSID().c_str()); + switchToSate(runningState); + } +} \ No newline at end of file diff --git a/src/nowifistate.h b/src/nowifistate.h new file mode 100644 index 0000000..f15d46e --- /dev/null +++ b/src/nowifistate.h @@ -0,0 +1,16 @@ +#pragma once + +#include "state.h" + +class NoWifiState : public State +{ +public: + NoWifiState() : State(true, false, 0) + {} + virtual void shortButtonPress(); + virtual void longButtonPress(); + virtual void enterState(); + virtual const char* name() { return "NoWifi"; } +}; + +extern NoWifiState noWifiState; \ No newline at end of file diff --git a/src/programmingmodestate.cpp b/src/programmingmodestate.cpp new file mode 100644 index 0000000..be52139 --- /dev/null +++ b/src/programmingmodestate.cpp @@ -0,0 +1,26 @@ +#include "programmingmodestate.h" +#include "runningstate.h" +#include "knx_facade.h" + +ProgramModeState programModeState = ProgramModeState(); + +void ProgramModeState::enterState() +{ + knx.progMode(true); +} + +void ProgramModeState::leaveState() +{ + knx.progMode(false); +} + +void ProgramModeState::shortButtonPress() +{ + switchToSate(runningState); +} + +void ProgramModeState::loop() +{ + State::loop(); + knx.knxLoop(); +} diff --git a/src/programmingmodestate.h b/src/programmingmodestate.h new file mode 100644 index 0000000..489fc8b --- /dev/null +++ b/src/programmingmodestate.h @@ -0,0 +1,17 @@ +#pragma once + +#include "state.h" + +class ProgramModeState : public State +{ +public: + ProgramModeState() : State(true, true, 200) + {} + virtual void enterState(); + virtual void leaveState(); + virtual void shortButtonPress(); + virtual void loop(); + virtual const char* name() { return "ProgramMode"; } +}; + +extern ProgramModeState programModeState; \ No newline at end of file diff --git a/src/runningstate.cpp b/src/runningstate.cpp new file mode 100644 index 0000000..a13cc80 --- /dev/null +++ b/src/runningstate.cpp @@ -0,0 +1,40 @@ +#include "runningstate.h" +#include "programmingmodestate.h" +#include "wpsstate.h" +#include "knx_facade.h" + +RunningState runningState = RunningState(); + +void RunningState::shortButtonPress() +{ + switchToSate(programModeState); +} + +void RunningState::longButtonPress() +{ + switchToSate(wpsState); +} + +void RunningState::enterState() +{ + if (_initialized) + return; + + knx.enabled(true); + _initialized = true; +} + +void RunningState::leaveState() +{ + if (nextState != &programModeState) + { + _initialized = false; + knx.enabled(false); + } +} + +void RunningState::loop() +{ + State::loop(); + knx.knxLoop(); +} diff --git a/src/runningstate.h b/src/runningstate.h new file mode 100644 index 0000000..374fb97 --- /dev/null +++ b/src/runningstate.h @@ -0,0 +1,20 @@ +#pragma once + +#include "state.h" + +class RunningState : public State +{ +public: + RunningState() : State(false, false, 0) + {} + virtual void shortButtonPress(); + virtual void longButtonPress(); + virtual void enterState(); + virtual void leaveState(); + virtual void loop(); + virtual const char* name() { return "Running"; } +private: + bool _initialized = false; +}; + +extern RunningState runningState; \ No newline at end of file diff --git a/src/state.cpp b/src/state.cpp new file mode 100644 index 0000000..bffae1d --- /dev/null +++ b/src/state.cpp @@ -0,0 +1,53 @@ +#include "state.h" +#include "Arduino.h" + +State* volatile currentState = 0; +State* volatile nextState = 0; + +void switchToSate(State& state) +{ + nextState = &state; +} + +void checkStates() +{ + if (!nextState) + return; + + if (nextState == currentState) + return; + + if (currentState) + { + printf("Leave %s\n", currentState->name()); + currentState->leaveState(); + } + + currentState = nextState; + + if (currentState) + { + printf("Enter %s\n", currentState->name()); + currentState->enterState(); + } +} + +bool State::ledOn() +{ + return _ledOn; +} + +bool State::ledBlink() +{ + return _ledBlink; +} + +unsigned int State::blinkPeriod() +{ + return _blinkPeriod; +} + +void State::loop() +{ + checkStates(); +} diff --git a/src/state.h b/src/state.h new file mode 100644 index 0000000..a8b77a9 --- /dev/null +++ b/src/state.h @@ -0,0 +1,29 @@ +#pragma once + +class State +{ +public: + State(bool led, bool blink, int period) : + _ledOn(led), _ledBlink(blink), _blinkPeriod(period) + {} + virtual ~State() {} + bool ledOn(); + bool ledBlink(); + unsigned int blinkPeriod(); + virtual void shortButtonPress() {} + virtual void longButtonPress() {} + virtual void enterState() {} + virtual void leaveState() {} + virtual void loop(); + virtual const char* name() = 0; +private: + bool _ledOn; + bool _ledBlink; + int _blinkPeriod; +}; + +void switchToSate(State& state); +void checkStates(); + +extern State* volatile currentState; +extern State* volatile nextState; \ No newline at end of file diff --git a/src/wpsstate.cpp b/src/wpsstate.cpp new file mode 100644 index 0000000..7c395a6 --- /dev/null +++ b/src/wpsstate.cpp @@ -0,0 +1,28 @@ +#include + +#include "wpsstate.h" +#include "runningstate.h" +#include "nowifistate.h" + +WpsState wpsState = WpsState(); + +void WpsState::enterState() +{ + //invalidate old wifi settings first + WiFi.begin("fobar", "a12"); + Serial.println("WPS config start"); + bool wpsSuccess = WiFi.beginWPSConfig(); + if (wpsSuccess) { + String newSSID = WiFi.SSID(); + if (newSSID.length() > 0) + { + Serial.printf("WPS finished. Connected successfull to SSID '%s'\n", newSSID.c_str()); + switchToSate(runningState); + } + else + { + Serial.printf("WPS failed."); + switchToSate(noWifiState); + } + } +} diff --git a/src/wpsstate.h b/src/wpsstate.h new file mode 100644 index 0000000..5460bb4 --- /dev/null +++ b/src/wpsstate.h @@ -0,0 +1,14 @@ +#pragma once + +#include "state.h" + +class WpsState : public State +{ +public: + WpsState() : State(true, true, 400) + {} + virtual void enterState(); + virtual const char* name() { return "Wps"; } +}; + +extern WpsState wpsState; From ff5dabad3bcc7b2a0cf13a15de9b2cda509298f5 Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Tue, 10 Apr 2018 00:16:34 +0200 Subject: [PATCH 11/24] add example to correct folder --- examples/{ => knx-esp-demo}/knx-esp-demo.ino | 153 ++++++++++--------- examples/{ => knx-esp-demo}/knx-esp-demo.xml | 0 2 files changed, 83 insertions(+), 70 deletions(-) rename examples/{ => knx-esp-demo}/knx-esp-demo.ino (72%) rename examples/{ => knx-esp-demo}/knx-esp-demo.xml (100%) diff --git a/examples/knx-esp-demo.ino b/examples/knx-esp-demo/knx-esp-demo.ino similarity index 72% rename from examples/knx-esp-demo.ino rename to examples/knx-esp-demo/knx-esp-demo.ino index ce98407..b07d8da 100644 --- a/examples/knx-esp-demo.ino +++ b/examples/knx-esp-demo/knx-esp-demo.ino @@ -1,57 +1,62 @@ -#include +#include #include "knx_esp.h" -float currentValue = 0; -float maxValue = 0; -float minValue = RAND_MAX; -long lastsend = 0; - -GroupObject groupObjects[] -{ - GroupObject(2), - GroupObject(2), - GroupObject(2), - GroupObject(1) -}; - -GroupObject& goCurrent = groupObjects[0]; -GroupObject& goMax = groupObjects[1]; -GroupObject& goMin = groupObjects[2]; -GroupObject& goReset = groupObjects[3]; - -void measureTemp() -{ - long now = millis(); - if ((now - lastsend) < 2000) - return; - - lastsend = now; - int r = rand(); - currentValue = (r * 1.0) / (RAND_MAX * 1.0); - currentValue *= 100 * 100; - - goCurrent.objectWriteFloat(currentValue); - - if (currentValue > maxValue) - { - maxValue = currentValue; - goMax.objectWriteFloat(maxValue); - } - - if (currentValue < minValue) - { - minValue = currentValue; - goMin.objectWriteFloat(minValue); - } -} +// declare array of all groupobjects with their sizes in byte +GroupObject groupObjects[] +{ + GroupObject(2), + GroupObject(2), + GroupObject(2), + GroupObject(1) +}; -void resetCallback(GroupObject& go) -{ - if (go.objectReadBool()) - { - maxValue = 0; - minValue = 10000; - } +// create named references for easy access to group objects +GroupObject& goCurrent = groupObjects[0]; +GroupObject& goMax = groupObjects[1]; +GroupObject& goMin = groupObjects[2]; +GroupObject& goReset = groupObjects[3]; + +float currentValue = 0; +float maxValue = 0; +float minValue = RAND_MAX; +long lastsend = 0; + +void measureTemp() +{ + + long now = millis(); + if ((now - lastsend) < 2000) + return; + + lastsend = now; + int r = rand(); + currentValue = (r * 1.0) / (RAND_MAX * 1.0); + currentValue *= 100 * 100; + + // write new value to groupobject + goCurrent.objectWriteFloat(currentValue); + + if (currentValue > maxValue) + { + maxValue = currentValue; + goMax.objectWriteFloat(maxValue); + } + + if (currentValue < minValue) + { + minValue = currentValue; + goMin.objectWriteFloat(minValue); + } +} + +// callback from reset-GO +void resetCallback(GroupObject& go) +{ + if (go.objectReadBool()) + { + maxValue = 0; + minValue = 10000; + } } void setup() @@ -59,30 +64,38 @@ void setup() Serial.begin(115200); Serial.setDebugOutput(true); - randomSeed(millis()); - - knx.registerGroupObjects(groupObjects, 4); - knx.readMemory(); - - goReset.updateHandler = resetCallback; - - if (knx.configured()) - { - Serial.printf("Timeout: %d\n", knx.paramByte(0)); - Serial.printf("Zykl. senden: %d\n", knx.paramByte(1)); - Serial.printf("Min/Max senden: %d\n", knx.paramByte(2)); - Serial.printf("Aenderung senden: %d\n", knx.paramByte(3)); - Serial.printf("Abgleich %d\n", knx.paramByte(4)); + randomSeed(millis()); + + // register group objects + knx.registerGroupObjects(groupObjects, 4); + // read adress table, association table, groupobject table and parameters from eeprom + knx.readMemory(); + + // register callback for reset GO + goReset.updateHandler = resetCallback; + + // print values of parameters if device is already configured + if (knx.configured()) + { + Serial.printf("Timeout: %d\n", knx.paramByte(0)); + Serial.printf("Zykl. senden: %d\n", knx.paramByte(1)); + Serial.printf("Min/Max senden: %d\n", knx.paramByte(2)); + Serial.printf("Aenderung senden: %d\n", knx.paramByte(3)); + Serial.printf("Abgleich %d\n", knx.paramByte(4)); } - + + // start the framework. Will get wifi first. knx.start(); } void loop() { - knx.loop(); - if (!knx.configured()) - return; - + // don't delay here to much. Otherwise you might lose packages or mess up the timing with ETS + knx.loop(); + + // only run the application code if the device was configured with ETS + if (!knx.configured()) + return; + measureTemp(); } diff --git a/examples/knx-esp-demo.xml b/examples/knx-esp-demo/knx-esp-demo.xml similarity index 100% rename from examples/knx-esp-demo.xml rename to examples/knx-esp-demo/knx-esp-demo.xml From 8277f4985b41d77f83d944de6d72bf640a2cb2c3 Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Tue, 10 Apr 2018 00:25:47 +0200 Subject: [PATCH 12/24] readd knxprod --- examples/knx-esp-demo/knx-esp-demo.knxprod | Bin 0 -> 41313 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 examples/knx-esp-demo/knx-esp-demo.knxprod diff --git a/examples/knx-esp-demo/knx-esp-demo.knxprod b/examples/knx-esp-demo/knx-esp-demo.knxprod new file mode 100644 index 0000000000000000000000000000000000000000..801f3be5f5b2af3bf28e8dd59d68178662c36a3e GIT binary patch literal 41313 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
#em31*a5Z_&N?+x-H? zRYI6*AMuE~c83#iaQHM>XYaL_$yr#Ny@Dgy`v%1{?_{@`ie3H6|02%wr%e^(*w%3C zS1*);u-C#%XO_DZ`{bobbG;$gbY?|T*;NnZdKhCaj&CXKU^vHk)|>%dZ2 zTKX4a`+j`8!ElLAI>M$k*R#X(V#^i)NE}Fe%WA4=WSQlNYh=m&D`9&|mOJD%qK0=0 z%XR3%+!`s9hH4tbPdDU627Dt!!N^l?Ve2HN{X=A>{f@e|@s2bhvKB<8n|p$j5hCT= z^0`6Y?&ml6qRR4c@XUZeoe=vHcZFtc-ujnG0Dwh23IOY=5&ZL@{5Q09@O*D??CNIj z^3Mja`u#S!Quj2RkLvax4dGw#zw!T1PR?)qE>BedUtFHw6#i##;@_-40o&xiDEwdg z7QeIRU#$PryZFs|{cptoOJCzR^M86U|1^4k(+EoW5A*+96ZoG*{%Pp^CUTbcA0q!> ZW{ Date: Fri, 13 Apr 2018 23:11:57 +0200 Subject: [PATCH 13/24] update .gitmodules --- .gitmodules | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitmodules b/.gitmodules index 6a68cc6..ca4cd4c 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule "src/knx"] path = src/knx url = https://github.com/thelsing/knx.git + branch = master From ad0eb38fa708fc02f3c2557095e82c6acf7b3bed Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Fri, 13 Apr 2018 23:19:57 +0200 Subject: [PATCH 14/24] update submodule --- src/knx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/knx b/src/knx index 2a169ee..39edb63 160000 --- a/src/knx +++ b/src/knx @@ -1 +1 @@ -Subproject commit 2a169ee66b5a95a11790a18bd1cd9378a49beaab +Subproject commit 39edb63cafa6d7889873e59e58d971e1d735921f From ca7876b73d786ce26c43c0ea65ff36ad769f5f76 Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Sat, 21 Apr 2018 21:39:24 +0200 Subject: [PATCH 15/24] update submodule --- src/knx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/knx b/src/knx index 39edb63..8d87bf6 160000 --- a/src/knx +++ b/src/knx @@ -1 +1 @@ -Subproject commit 39edb63cafa6d7889873e59e58d971e1d735921f +Subproject commit 8d87bf6de538cfacf6c56259b9076e5372f0971c From 082a029aaf8aebad836551ca00e60b86df1d7cbb Mon Sep 17 00:00:00 2001 From: thelsing Date: Sat, 21 Apr 2018 21:51:41 +0200 Subject: [PATCH 16/24] Create LICENSE --- LICENSE | 674 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 674 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..94a9ed0 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + 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 3 of the License, or + (at your option) any later version. + + 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, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. From 21bda0613340d3468c143755395d4b6c21708ea6 Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Thu, 26 Apr 2018 00:24:09 +0200 Subject: [PATCH 17/24] update supmodule --- src/knx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/knx b/src/knx index 8d87bf6..d710ec3 160000 --- a/src/knx +++ b/src/knx @@ -1 +1 @@ -Subproject commit 8d87bf6de538cfacf6c56259b9076e5372f0971c +Subproject commit d710ec327b3b653d4fb3032c41fa717acedd26b9 From 2a292ae314b34d9b1d71ae08d1f30ee5915733cd Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Thu, 16 Aug 2018 23:58:57 +0200 Subject: [PATCH 18/24] switch to visualgdb, update submodule --- .gitignore | 4 +- examples/knx-esp-demo/knx-esp-demo.ino | 2 +- knx-esp.vcxitems | 89 ---------- knx-esp.vcxitems.filters | 224 ------------------------- src/esp_platform.cpp | 71 ++++++-- src/esp_platform.h | 9 + src/knx | 2 +- visualstudio/knx-esp.vgdbproj | 170 +++++++++++++++++++ 8 files changed, 243 insertions(+), 328 deletions(-) delete mode 100644 knx-esp.vcxitems delete mode 100644 knx-esp.vcxitems.filters create mode 100644 visualstudio/knx-esp.vgdbproj diff --git a/.gitignore b/.gitignore index 2ba06ea..e5be27a 100644 --- a/.gitignore +++ b/.gitignore @@ -243,5 +243,5 @@ ModelManifest.xml # FAKE - F# Make .fake/ -/CodeDB -/VisualGDBCache +**/CodeDB +**/VisualGDBCache diff --git a/examples/knx-esp-demo/knx-esp-demo.ino b/examples/knx-esp-demo/knx-esp-demo.ino index b07d8da..20de32f 100644 --- a/examples/knx-esp-demo/knx-esp-demo.ino +++ b/examples/knx-esp-demo/knx-esp-demo.ino @@ -1,5 +1,5 @@ #include -#include "knx_esp.h" +#include // declare array of all groupobjects with their sizes in byte GroupObject groupObjects[] diff --git a/knx-esp.vcxitems b/knx-esp.vcxitems deleted file mode 100644 index 2b91085..0000000 --- a/knx-esp.vcxitems +++ /dev/null @@ -1,89 +0,0 @@ - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - true - {83464ca3-e0d0-4486-82f4-f658f31ddc69} - - - - %(AdditionalIncludeDirectories);$(MSBuildThisFileDirectory) - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/knx-esp.vcxitems.filters b/knx-esp.vcxitems.filters deleted file mode 100644 index e304b22..0000000 --- a/knx-esp.vcxitems.filters +++ /dev/null @@ -1,224 +0,0 @@ - - - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;s - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hh;hpp;hxx - - - {0ee56052-c43f-46b9-ab25-f805ed81e840} - - - {e0ae5c46-92be-4bc5-bcae-4e831818010c} - - - - - - - - Header Files - - - Header Files - - - Header Files - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files\knx - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files - - - Source Files - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files\knx - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - \ No newline at end of file diff --git a/src/esp_platform.cpp b/src/esp_platform.cpp index 2ba4115..0e14a56 100644 --- a/src/esp_platform.cpp +++ b/src/esp_platform.cpp @@ -3,6 +3,8 @@ #include #include +#include "knx/bits.h" + EspPlatform::EspPlatform() { } @@ -72,17 +74,6 @@ void EspPlatform::closeMultiCast() _udp.stop(); } -void printHex(const char* suffix, uint8_t *data, uint8_t length) -{ - Serial.print(suffix); - for (int i = 0; i 0) + printHex("p>", (uint8_t*)&val, 1); + return val; +} + + +size_t EspPlatform::readBytesUart(uint8_t *buffer, size_t length) +{ + size_t toRead = length; + uint8_t* pos = buffer; + while (toRead > 0) + { + size_t val = Serial.readBytes(pos, toRead); + pos += val; + toRead -= val; + } + + + printHex("p>", buffer, length); + return length; +} diff --git a/src/esp_platform.h b/src/esp_platform.h index 9577d93..4d5bd08 100644 --- a/src/esp_platform.h +++ b/src/esp_platform.h @@ -25,6 +25,15 @@ public: bool sendBytes(uint8_t* buffer, uint16_t len); int readBytes(uint8_t* buffer, uint16_t maxLen); + //uart + void setupUart(); + void closeUart(); + int uartAvailable(); + size_t writeUart(const uint8_t data); + size_t writeUart(const uint8_t *buffer, size_t size); + int readUart(); + size_t readBytesUart(uint8_t *buffer, size_t length); + //memory uint8_t* getEepromBuffer(uint16_t size); void commitToEeprom(); diff --git a/src/knx b/src/knx index d710ec3..7d86f63 160000 --- a/src/knx +++ b/src/knx @@ -1 +1 @@ -Subproject commit d710ec327b3b653d4fb3032c41fa717acedd26b9 +Subproject commit 7d86f63f8e8399a785278e50532805630fb4e07d diff --git a/visualstudio/knx-esp.vgdbproj b/visualstudio/knx-esp.vgdbproj new file mode 100644 index 0000000..b90ff2e --- /dev/null +++ b/visualstudio/knx-esp.vgdbproj @@ -0,0 +1,170 @@ + + + + + + Unknown + + + 6165cd6a-91a4-49fa-977a-48f22086ca8e + true + true + ..\examples\knx-esp-demo + knx-esp-demo.ino + + + + knx-esp-demo.ino + + + + + + + + ..\.. + + Output\$(PlatformName.defuse)\$(ConfigurationName.defuse) + false + + + + + + + + + + + + + Default + + + + true + + + + + Unknown + + true + true + true + + + + false + + + + + NodeMCU 1 0 (ESP-12E Module) + + + + + false + false + false + false + false + false + false + false + false + + false + false + false + false + false + false + true + false + None + false + false + main + true + false + false + false + 0 + + + gdbstub + + 74880 + 115200 + 50 + !DTR;RTS;SLEEP;DTR;!RTS;SLEEP;!DTR;SLEEP + Auto + false + + size4M + freq40M + QIO + + + + true + Auto + 0 + false + false + true + false + false + + _estack + 0 + false + true + + + esp8266:esp8266:nodemcuv2 + COM7 + + + + CpuFrequency + 80 + + + VTable + flash + + + FlashSize + 4M1M + + + LwIPVariant + v2mss536 + + + Debug + Disabled + + + DebugLevel + None____ + + + FlashErase + none + + + UploadSpeed + 115200 + + + + Sketch + + + + + \ No newline at end of file From ecce8ddd3c0af1bbb0b19b6b228b906933c6bf08 Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Tue, 4 Sep 2018 21:12:50 +0200 Subject: [PATCH 19/24] add save + restore callbacks --- src/knx | 2 +- src/knx_facade.cpp | 30 +++++++++ src/knx_facade.h | 11 +++- visualstudio/knx-esp.vgdbproj | 119 ++++++++++++++++++++++++++++++++-- 4 files changed, 155 insertions(+), 7 deletions(-) diff --git a/src/knx b/src/knx index 7d86f63..ddf99a9 160000 --- a/src/knx +++ b/src/knx @@ -1 +1 @@ -Subproject commit 7d86f63f8e8399a785278e50532805630fb4e07d +Subproject commit ddf99a9b96572e7dec97e8a04fc9ad909728bbec diff --git a/src/knx_facade.cpp b/src/knx_facade.cpp index 89a6e7a..898f31f 100644 --- a/src/knx_facade.cpp +++ b/src/knx_facade.cpp @@ -145,3 +145,33 @@ uint32_t KnxFacade::paramInt(uint32_t addr) return _bau.parameters().getInt(addr); } + + +void KnxFacade::setSaveCallback(saveRestoreCallback func) +{ + _saveCallback = func; +} + + +void KnxFacade::setRestoreCallback(saveRestoreCallback func) +{ + _restoreCallback = func; +} + + +uint8_t* KnxFacade::save(uint8_t* buffer) +{ + if (_saveCallback != 0) + return _saveCallback(buffer); + + return buffer; +} + + +uint8_t* KnxFacade::restore(uint8_t* buffer) +{ + if (_restoreCallback != 0) + return _restoreCallback(buffer); + + return buffer; +} diff --git a/src/knx_facade.h b/src/knx_facade.h index ce90334..435873c 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -5,7 +5,9 @@ class RunningState; -class KnxFacade +typedef uint8_t* (*saveRestoreCallback)(uint8_t* buffer); + +class KnxFacade : private SaveRestore { public: KnxFacade(); @@ -28,6 +30,8 @@ public: void hardwareType(uint8_t* value); void version(uint16_t value); void start(); + void setSaveCallback(saveRestoreCallback func); + void setRestoreCallback(saveRestoreCallback func); uint8_t* paramData(uint32_t addr); uint8_t paramByte(uint32_t addr); uint16_t paramWord(uint32_t addr); @@ -38,6 +42,11 @@ private: uint32_t _ledPin = 16; uint32_t _buttonPin = 0; Ticker _ticker; + saveRestoreCallback _saveCallback = 0; + saveRestoreCallback _restoreCallback = 0; + + uint8_t* save(uint8_t* buffer); + uint8_t* restore(uint8_t* buffer); }; extern KnxFacade knx; \ No newline at end of file diff --git a/visualstudio/knx-esp.vgdbproj b/visualstudio/knx-esp.vgdbproj index b90ff2e..0e004d5 100644 --- a/visualstudio/knx-esp.vgdbproj +++ b/visualstudio/knx-esp.vgdbproj @@ -1,5 +1,6 @@ + Debug @@ -10,7 +11,7 @@ true true ..\examples\knx-esp-demo - knx-esp-demo.ino + Sketch.ino @@ -25,7 +26,7 @@ ..\.. Output\$(PlatformName.defuse)\$(ConfigurationName.defuse) - false + true @@ -40,6 +41,60 @@ Default + + + + 115200 + 8 + None + One + None + + + 0 + false + false + true + ASCII + + + 255 + 0 + 0 + 0 + + + 255 + 169 + 169 + 169 + + + 255 + 211 + 211 + 211 + + + 255 + 144 + 238 + 144 + + + + 16 + true + true + true + true + 0 + + LF + false + true + true + true @@ -47,22 +102,76 @@ - Unknown + True true true + + Enabled + true + true + true + true false + + apiModeling.google.GTest + core.builtin.BuiltinFunctions + core.builtin.NoReturnFunctions + core.CallAndMessage + core.DivideZero + core.DynamicTypePropagation + core.NonnilStringConstants + core.NonNullParamChecker + core.NullDereference + core.StackAddressEscape + core.UndefinedBinaryOperatorResult + core.uninitialized.ArraySubscript + core.uninitialized.Assign + core.uninitialized.Branch + core.uninitialized.CapturedBlockVariable + core.uninitialized.UndefReturn + core.VLASize + cplusplus.NewDelete + cplusplus.NewDeleteLeaks + cplusplus.SelfAssignment + deadcode.DeadStores + nullability.NullPassedToNonnull + nullability.NullReturnedFromNonnull + security.insecureAPI.getpw + security.insecureAPI.gets + security.insecureAPI.mkstemp + security.insecureAPI.mktemp + security.insecureAPI.UncheckedReturn + security.insecureAPI.vfork + unix.API + unix.cstring.BadSizeArg + unix.cstring.NullArg + unix.Malloc + unix.MallocSizeof + unix.MismatchedDeallocator + unix.StdCLibraryFunctions + unix.Vfork + + + -analyzer-store=region + -analyzer-opt-analyze-nested-blocks + -analyzer-eagerly-assume + NodeMCU 1 0 (ESP-12E Module) - + + + + + false @@ -110,7 +219,7 @@ true - Auto + Disabled 0 false false From 6334bfe3cb99585ee61170154007b9472cd33588 Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Fri, 7 Sep 2018 23:41:58 +0200 Subject: [PATCH 20/24] update sm --- src/knx | 2 +- visualstudio/knx-esp.vgdbproj | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/knx b/src/knx index ddf99a9..f50f03a 160000 --- a/src/knx +++ b/src/knx @@ -1 +1 @@ -Subproject commit ddf99a9b96572e7dec97e8a04fc9ad909728bbec +Subproject commit f50f03ad6edaa73c6bc41496de696e1d7306b30b diff --git a/visualstudio/knx-esp.vgdbproj b/visualstudio/knx-esp.vgdbproj index 0e004d5..4234096 100644 --- a/visualstudio/knx-esp.vgdbproj +++ b/visualstudio/knx-esp.vgdbproj @@ -43,6 +43,7 @@ + COM9 115200 8 @@ -54,7 +55,7 @@ 0 false false - true + false ASCII From dc362a79c3bc2f1933275fa7c75d872bf5c7f183 Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Sat, 15 Sep 2018 00:22:30 +0200 Subject: [PATCH 21/24] add writeMemory to facade, remove debug output from platform --- examples/knx-esp-demo/knx-esp-demo.ino | 6 +++--- src/esp_platform.cpp | 4 ++-- src/knx | 2 +- src/knx_facade.cpp | 8 +++++++- src/knx_facade.h | 1 + 5 files changed, 14 insertions(+), 7 deletions(-) diff --git a/examples/knx-esp-demo/knx-esp-demo.ino b/examples/knx-esp-demo/knx-esp-demo.ino index 20de32f..b7ae38c 100644 --- a/examples/knx-esp-demo/knx-esp-demo.ino +++ b/examples/knx-esp-demo/knx-esp-demo.ino @@ -34,18 +34,18 @@ void measureTemp() currentValue *= 100 * 100; // write new value to groupobject - goCurrent.objectWriteFloat(currentValue); + goCurrent.objectWriteFloatDpt9(currentValue); if (currentValue > maxValue) { maxValue = currentValue; - goMax.objectWriteFloat(maxValue); + goMax.objectWriteFloatDpt9(maxValue); } if (currentValue < minValue) { minValue = currentValue; - goMin.objectWriteFloat(minValue); + goMin.objectWriteFloatDpt9(minValue); } } diff --git a/src/esp_platform.cpp b/src/esp_platform.cpp index 0e14a56..017ff11 100644 --- a/src/esp_platform.cpp +++ b/src/esp_platform.cpp @@ -76,7 +76,7 @@ void EspPlatform::closeMultiCast() bool EspPlatform::sendBytes(uint8_t * buffer, uint16_t len) { - printHex("<- ",buffer, len); + //printHex("<- ",buffer, len); int result = 0; result = _udp.beginPacketMulticast(_mulitcastAddr, _mulitcastPort, WiFi.localIP()); result = _udp.write(buffer, len); @@ -97,7 +97,7 @@ int EspPlatform::readBytes(uint8_t * buffer, uint16_t maxLen) } _udp.read(buffer, len); - printHex("-> ", buffer, len); + //printHex("-> ", buffer, len); return len; } diff --git a/src/knx b/src/knx index f50f03a..fd2334a 160000 --- a/src/knx +++ b/src/knx @@ -1 +1 @@ -Subproject commit f50f03ad6edaa73c6bc41496de696e1d7306b30b +Subproject commit fd2334a4af59e9cea5325d399afc639517a5d41a diff --git a/src/knx_facade.cpp b/src/knx_facade.cpp index 898f31f..0c4956a 100644 --- a/src/knx_facade.cpp +++ b/src/knx_facade.cpp @@ -9,6 +9,7 @@ KnxFacade knx; KnxFacade::KnxFacade() : _bau(_platform) { manufacturerId(0xfa); + _bau.addSaveRestore(this); } bool KnxFacade::enabled() @@ -61,6 +62,11 @@ void KnxFacade::readMemory() _bau.readMemory(); } +void KnxFacade::writeMemory() +{ + _bau.writeMemory(); +} + void KnxFacade::loop() { if (currentState) @@ -108,7 +114,7 @@ void KnxFacade::start() pinMode(_buttonPin, INPUT); attachInterrupt(_buttonPin, buttonDown, FALLING); - + switchToSate(noWifiState); checkStates(); _ticker.attach_ms(100, doLed); diff --git a/src/knx_facade.h b/src/knx_facade.h index 435873c..19fb3bc 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -21,6 +21,7 @@ public: uint32_t buttonPin(); void buttonPin(uint32_t value); void readMemory(); + void writeMemory(); void loop(); void knxLoop(); void registerGroupObjects(GroupObject* groupObjects, uint16_t count); From a73fa612b4d4132965889de2c5b011f5e8dfe9bb Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Mon, 8 Oct 2018 21:51:27 +0200 Subject: [PATCH 22/24] add bme680 example --- examples/knx-esp-bme680/bme680.knxprod | Bin 0 -> 41039 bytes examples/knx-esp-bme680/bme680.xml | 98 +++++++++ examples/knx-esp-bme680/knx-bme680.ino | 257 ++++++++++++++++++++++ visualstudio/knx-bme680.vgdbproj | 283 +++++++++++++++++++++++++ 4 files changed, 638 insertions(+) create mode 100644 examples/knx-esp-bme680/bme680.knxprod create mode 100644 examples/knx-esp-bme680/bme680.xml create mode 100644 examples/knx-esp-bme680/knx-bme680.ino create mode 100644 visualstudio/knx-bme680.vgdbproj diff --git a/examples/knx-esp-bme680/bme680.knxprod b/examples/knx-esp-bme680/bme680.knxprod new file mode 100644 index 0000000000000000000000000000000000000000..05f7fbe60508201790c35e0fa6aeabc972d3427a GIT binary patch literal 41039 zcmZs?V{~NQ6aO3Aww-kBNhY>4v8{hY6=lF7FhD>+U_fk&Tx44`xa!H`KtRl*p+S&9C_sMNy6M{(IysvDV z!-0@|ksu(TAUGh^`g?>5J+q)yO(Oc7`g8x!^{nI6_H?3_`hj0?!&kFgb|Lgp3yW4M zY3xsyuWh5LOe4c^6D9-{KncwzEJ13di9~RNR4{Xkq}vmJ4CIy)6fFJSi-?RVMqQFi zDpsyks>VH*w(YZfW0Yc`)Xc?2)#b6O^t0>p^z-?2vkPEyAU5YLrTX#V|E~Mg6EdQa zAvtn(DWEoZr+)vp>+|sQq3ikd>7PjbF#=B#)aD zN%H~viKXvt9^F}FPRoy!dGY6tPUL3~LO}`{4!zvlEAp|XC*k)jOX$N=$2|UpLF%f$ z6Wm98PGVv`f%eYCXrRscn=4I1!G;A54zzms#gzq_vz{GN0ovRkSmGH%SU}qIO@JVm zW_kOh=gPkSdm}C~s5yh(RN?D(nMuUqDb&$}s!Tc@J?@OfR3Z}xInDYi7?>oTUdn_kK@EmQ6OLOuu-tZ;1c}fEQz7m0 z61W9O(B)d0#xvB)e3+Hj29hcQDZb>UWb9KS(CDzyyd5PB5$C;}v_I%56|JIWG}C3K zGG3|+ChDVoq3%+GEeFb#ISEX_+?fQ`b<_|zTY{*HZxDCOV666VdcH8J+tLs^1Sdh# zRM=RA!1Cx;QnES3UNiFDn0ti&D6q4(S=-@GGSWx{u?03P?HMB)y7-*yi>RT{dIM+s z`00u^iint;VPN}awGOU`OF^XAqewSEyy-I5UTBc+6t^5)37b&Jasd+7(4@XLb6bm8 zfQQrIEx)*`mOBNt5f8Vp+&n#s-I5c%fdkywOG=mkx3b*4T`%HqSwVKhW`@%K28p3h zgw_I^wH*ayvd=59lvn@CY6A=?MEMGbgsYuyZtXIk!j83;SM3z53i8vpDzsHt7Q(hO6|1Q=ZE<$3Xm}2vh-)cy9a!n~tDxJUK zK5SvLvmdRk3^OE!aqT0N>Z$kua%J<>*2v^MQmsxSglDfmK!BS5C~Sa+;pwK%S4e|_ zDyJtoD;zgrzzCg`d-aH%LNa`SjX`5oQN*4JD?WRxg3>bAl15XYZ1`n3mADVh0RirW zAZpDuiU9R5z;L6Gp=Vs9cMa{@5C`{K#aJ8S=+H#W&rOY1%Rb$bHW#ZM$E-)@SmVlQ ztGgx;Ok_Fmcqp9)OOL8)d!T!|{=oX#@vm@q+1NKo^B@_EqKHF3st80J39=3)xgsuU z-;a9OrH#elae4DR(lLeG4*}GnPK+Y~x$pCI`fcVc5xjlyP~YDMK7N?-``jw=t$?dK zX_RB=rqb@1@K-5xu{~{VLG=#~#b|^X6c*t0%9?m%d4EEo?sPpDJ?;7-NAR4CjAh8d zUHLuppqi)BoKR#;LJ#73or+vkm4apsUcYsb8e?KO)c4oagPnu!il z)9m5Erm|=7{t&#SBMLU?vVdeid!N_PrJZ13ZU^xA8i3H8D|8`|QR5K22u{Ok{*+h$=6lI(EyFj7$g~F)7I9uDblr8U z+_W^T)D#~WX^2#z`W8h*_62|v)GiFdB;qO+Je#tIE@J&65`IJrRKyU#m1D|?yu7-7 z2j|>MlElX8cGWr_oiv`L?c6nP(puX~)U374w{X>Pw`q+EpX^i@v2Np35aUA3Ml`q5 zn@u3pXxb_Sk+>KteU)Z|C*5q*^lW&o>*McPIg%}DO;8Q*4^2#j_UUUm%O5pD4FFD< z6&ndmMez5k&l;&2o(4hiy_=LRoSc)bUvS#oP|IXVdDRC`6eCP=j@-fCM~BO#v89V( zfY^cJHpds7W{m^$q_N2bFu7>45Ak0;C^D$rD|w0EG|*hW<6|3nX+l z%laBkEPc}fx=V)PIKHM#l{;z@q`dgBOsCgK)phorFH>f1 zYe`}egVL;T0_fH0HSPUudscx*--qpR5?;W7OOMSC zXy%e^4yf8o4$lHN^gBY)c?ckbbd9<4@5OeORRcI27@PK(!tmH=&elMI9o0fsFO>!U z@nJZ7737g}F&W*78h_GCm}kk&`8mk+;N_tbrxgSk<#*YlC5YOx%Z~p9pI*V-t1;~< zqGf=*g7n*=YKmCb9#hD*q}#*Baa?*k@Z&_kJa35KmQI6XJfzj(%g~5U&XSwKS!|Le$7IflyZj_Cr2Xiti|(a zTj>~hBZ#|!m*uH_YA|)MwqPDM9bYlt|2xDFWiLRXcHUi15#D9zHF3OHq?sOtIHr8# zU0z`*-{|L+R+D&2JskzNho9WOclfiy(Uw)O${P*f-5X*!%3GOLr?7^PXuqc)%~IJ^ z@yHu#DJ7$d1(2wjP6&OTa>dSOlH_1?6LQPLd{Kr90%T)e^tq`qx@9aaMv+S;P>YDL zO*7ylrt}s?*a|?^@$Zo0wEsS{3pu-pX?Yv(V4Xp&v5{NuW0Rzf_ekAJG}C8$p$oyt zN1iP2wfu|bj#Eddjg=GYiX*s9AvDO|>kX#jlkf^OJ(jxwK&+xIAvsGVH%o;K?12Fa zTT#ryEF_K7OYDhw3v=Z+Sj76hvB1xZS{1kX4nHL4-_UA^S$;f)=pc#pn_xLm&9Y59 znf=PwL5bJsPZpj0wX83Uw}df$Og^|qXt{GMEqW7Bbc#PV8IP=N79vG&B)jrky1K`i zpIR{aZwdwm>aALTG8aAm0^Ia^$JeH=X4lmzOdcZ7M+fz%TE7Nc{ONDRg+5G8f65ht z09n*o)qYYalG#zr#K?E26 z@5?=Daf}Wm;)MY$thyoZYcV+pm%PFh#CMea^$E;{1#McAaCDOHUjENKnf>;Tl>H+K z%xZ%s#g)zq>7=V%=iDMxK!A~}G)~+igwi1ebcPIxeR$Y9yogs&N&Q*TIMrx_KFc=UH+r`e>YPL@7JP%*nmXrGL+CmT zQ`D!~q0Td-0vmxbOmLK8RaroOHQ zq};!aMA`w{S3z=_?4cl{hDk&fNJS*tiF8>WoLL!rBVsL!Yit~JBAsF_Zo=Ch8D!jl zBs+x9!eJUwf-Bsv%bLx(=VyCm7y%z~r$e2erp_X+k6XiJ8sSybWOgB;XjJLHSg{&m zxE^Hdr||<7{l{Q(a$&1RF&twCgOn?nNd!(ht8J5A##CcqL;SorumXfj1(jZ<2ee(6 zG40f(Bww~$oTRbDug_;v%m$Rp#xoot$VFh9R|K*~8B-@}D3!~sZ)iK(mY~O1b`7TA zOx50!JSMhP4|vDz)OQ-YxAx1rwGnr>xV`QM`2OHBskCtIHf__ zTWKfLUoR(7EAgw5<|;kFu=j-gW5>9dAG*@ zX1^+`lQyP2O3J`(zj&{p@e4?;Xe_;WtQ^QaaQe(j3fxR-pIW!-%K-!QfS?YXXeA`G z24mO>8mSOyETUNoRgqcDjGt&HLMZA^StXr1BQr}nFJfJND>i6r!7QRd;2mUViurp~ z-+DZydl_M#tgswxM*J%K9JbHiv{ZFWdx(Eq8~clNJOry3Uv--zI6&roUrjBvI>25- z-gIFua6T(@9|*Z}Pq%}G@+MQ3i+J1s>bOPnC&n2!F(0 z*Zn$R8sv@7s=cTcb!&OlU!nML`;$r3rm;sM;FlbU0zuT#6u_2-)+k_Dj>M*^hXB*WNhFmF3?IJp&ByWVtZ3XkIafFgTLp37Ki z3SRU0(bo5T*%9;xJUymm^q$4;L|@<&S)|z94C9$ZH=s|#L6-5PK_hF`YF^1~ixmr8 zMW{XdHi+3<3&1}MdMlqUzsu)~et610j?+oM+fqpPJwH$tLk}8x-JGS9wY*zjBpsd! zjp2Mz^{>KXS0#p~3v#@Bb*2?UyL)qOR_wL-FDb8yjJ@C4oHph8`-#DqcydVyKuckm^b5=XLKY15lcUnQ( zB9GXe;8)Vudw6lwX!&y+ZvgtK5iymaRE6D?|7-hNrg6{68>1EX#N~i_#s9(Sh)B{D z%7djG{$zAiPrvhE2CUM+x*7iviGB+oA@#m?Bl^38E9tGt}5*|CZ?c#O~( zZjQE5tSYy$;-tZ*Z@YS1GY@=`1*0h}wV`yeT(d~i;4IaPK{XCHz^+p3%4*(dJ9NM2 zUOKdE1@g$FutE<|sz60(TH0H)+btL{Eb2D~krLa@W`oK_1Tx0=L$ituF>0*zbi*aM zC-&=A^-0)8Q19+ztGl;C+;XEBX4ZJPO;2qS(JX4YikdOeY8}(7?6*f6GJx7;UJEYqlk^yu z+qFeULT~{3gcXs9>qNmYt=st!mdz}^)@yS2UKUP!hzUz&mJZKP2Ig2LtSPqMog2h7 zO3hWq2kG*b2_$}h>eOKUi}HD`(esdC5~3PXy(w2D3wWQDjk`dqKSqP3NYmG-G^Y-2 zi=Zn7J&+t;4LYSHm1)dTn;MsEsPw)~7QtNkf%G0>GHZpn3v5s&#o)hvOIRQL z&lKsO%3UTr;!JT*+h*weCmnJYHt@a|8%kTpH7k}~E*S)qzI~%gt7e%-r;e**YpG_0 z5%81UT>E_r3ilWIE8Ah|1wv<99iA}H-jXmCPp9=txFuP>BOJWcu~@$70Sr{?6C#Pe zUjDVdw3~FGe=O2SE~1YXhTVYB%KP%2%mbHUf!bzuwDpq9b8T!rZeKc!4Vk`A@!EP; zg~=KcChD;?iHyJ7->44m?=yhBtDq9()u|5k;v@INyBJ165E7VcD8Ix?5U>P>NpR6# z8zG9}hZA7HIH#?BRN384Ay;E?YHCj;X@F{%J_FX|*b~xS*yA$a!lQIB>Jc0$j)X+MTBPatp}_@7{Cd?-Q^)qnnsEu@FrZ z5a9h;5=>I;teIlkpTb1IPsSZt5d8^PGex#PC5DLMiMQg1B5G9%pHmF(qGXwlttz0| zWevFX(QLjTek+k<6hV1d{Mq?XUn}647MJ3qIC<Z}i zfDBeA6vE*QyA?eS;RknLrzj>-C}u~G(J}UGxaru$9Yn2sca~HYRJ0!l@A1kEEivQp zO{2j0pARZJV@$1uu1IX$%=hk`Sj@v9wREn=6Am?Aa#^ZWfk(y$x3*@RE_6;%Tanz* zqYX1tDzZ82=KF*0ETX#WQ8l*Psp%dE@-!M)S@gcG9}hLfR309&*Ug6XYS)0DhqL4y$Q&PwB92dYR*3_ zs@pj~Wo-$+Us2Pwlj5bOnMbFUAk553@hgoLJ@ow2)MzIA9IfNoS;fcw_r0DGGW&9J zhqk^!vYiQyCcC%u=!dTWzh!+XYoZzb>6E{S85t~_*gT?Txp{udwmD%Mv!KW2>xTwL z{VQ;mjC#-IqI@@DOf*DI*M%O}Prx{YnM17HHpu|qrmwOfXhC{!%_wwBLB&c&RiA0h zY@1CQr6+(rztxw5l{~L=9S%atL`+uZP{_vKdf23UmoSYx%Cjn@G1r3m_4U_b&ds=M#=cja;Qs)ga+FCr3kNb?4W-$P2h_SDY>lF&Y^ zZImk6bh_XSf^Zd`{>lh|NluxCoR$H(KfyX_P6a0{dNx073bP>S#a~_dSllk`{y2es ziSLk(fSd04$+3$!R=YI@=k6nKpdLQ(&9m4CmyRLZ$}HgR{cj6g*UCOGN|9|Afvq9* zuj=-*wK$P3cpTfv>oCNfpc7(>6{ubH8{~j{|6lc}j~x$Htqf(mgI28vmwKx+L6oJ> zCxedWBt4z2qC*u`I!1hJ8R61rlIUUqsZOEw6@y~bMU(rj=yl83NVJt|v@x+eO3LlQ z7!1Oa!le;$bRJTuFvOj@LQ<;j`E(Td>?&%h5^4j&vDw($t9;U7k8yNyv2=O)BYMiB zBiT8%GkmpR`AbLnR5LPV?`Z;7?2C6}-yW}4F#jY{TC9WhCs2*&?Q+PSF7UVeq68K^ z=b{nbgY+0rbH|xSH65*2*)^fRcv8*L#dboe1Oo&?W}p;s?+qKlc^djqSLzQz9TvrE{2 z$dJztnn^FF+=98a%ATnE+W{&f0imUSV;@&apw6wO9iYa4y6tHr#GF83fzH7{#j8F2 zZe8%E^eoeVkv1n{}Lpj z*VrT;>uH#vsNwU$NC?fA`@Q9IzRrYM*=`ud8WS#|iUtEi3!TI?Y~A181{|v?tOUBQ zO7x1r?OKF8)8WU*Dt7S11!7-;IFrr{di>-}gC0I^~-vaLQxhJKe{b%lnLu^P&~Koo{xwQ5SYs6M1*!mWay8$+{RgrZ?pr z0*9NX93Ud}AJMn3`!k$NxE%lX~A` z!~?o{O=poS7Co*LKeV&tRx4EG3@ZvglnP10ngr|HnVkn;KUw}gHXH|zMcQWo?>Vky zi*vD&eAFiLn=DyNfEvM4e+m@?mb^``&dA8(h=L-=W24)Bz5EkFCif8o`5A@s`cXKU}*14kD^YJe>)aCY*5PSI5MQ3PIxN2SC^vf zay`X(dA})@{MJnXU&6S$AQzh+I#>^$*`a3sq6u#p11*^jbAaRlO06Py24u)Xd;a!_ zzesAnTqd^2il`)3CK8??P|_%nBSj016*E|t12%=Ojmp@pq@P-a2HG2_j{5c)TjL9@ zdF~?@0%yw18xTx`XUeDuH`RZ*<=<)xR@Txx2+TF+<7u8CWrzbD~`J z5v9f*DutaP#-2`MH<+04M66rO&=3!>IuSM2Nr#|zHP(sGmnAeHoqmAy=^*dvfK-zL z=TfQL%`?`(!WME@Ea2)}f204kGzF?W1~ZX93Bk6Aeh~r}Zy*y&)HTdgXNaO%eWLc2 z*MTny1%=_p_COyw*tzzm;O}-x8adV}l1b$$5=cZd5^OlU{A{8BuZe)cdvgZlXZIMC zDE?#q1I6PJArUY1qG>E*6b&%gapW8^*pG^2h5RwtC58Ntqmkh7#)3(6|8+v}1mar6 z-1Kv>l&aTpVNeiTGa_!h`ElfYgv!IHnMfsj zFba(P0YxHoJiW7|i3HonG15_2dovPlj+!KVEet4`jME7ynL4gtm~H@d65iU-#cx`PgDl1Al;L75Eq&yab}k)~mJVpy90zETRdXuVQmTAtg=Lt3ui z%G2{J>6Rdjv+R)|^t1f(jE;LGP;Xc>&`)wCxL860uJ)pWOcN{>Xi_fxUc2;Xbllj&9->gLKCaN`9F|FI%cZmJ%% zav%gm9awcWU5z~e1EVfjL16Q%3kP-wP8-{1wY?d07tTUVRGhmWOsFyjtLIlxt`2BL z$Psq18p#;u3|WFY$pq$1Sdbr$Eo3X0mNmOusR%vFF`T8C$o5xFPNk2UC_>;@>J-ir z^+=s&>8oa{O0k}PH0dYA3ntvJztY?^0ks%(=N8P6TtcX}AC6*MpzLQqKft`x=K z#$E$bImdU{zdgd=B)Auvzzj*{gb~Ah0s`}b6~jZat7E?V>gghY_$zhP6nd~Ub5)`2 z**Y8D-vX2HnzzZ-SGX^Hcbqe_l+$f9AM|c1al>ZUbGa~zWE@)mUz5L$GjOb&&Ny&P z6@&FZ@2jA>n?{~1b&p1VQ6Xi=nN~h&$9a~R5Ths!Sw!?-IsOv^eLAE1)wXju5^LI@25nV0RH@r|f8_DZ?3-2)|x!!t4BdJXfJom*Gu zehwk*=5F4)`g9aTLIpm}x&;ka(uIYE5Qa`|-gXCeFnEc0FW*yfqv*28eEWl9WWaCx z5~A=RQ%ru4$a_G7BfE*8Oben?LLtA6Pt4YE!KUqIUK2kbnW2mhkpwvq{L{w(I+3&8 z!*26dks2o)#)TyM{>3L4+;Oun&_Fa*18DY_07%3qQ|Dy2E<`IK;z*Y8iZkNHVA`v+ zoDProbZ8W)81#5C{*7oPz}S)BmEvoU1+0^RQ%XVOhDZ8##4`e?)9~B8g0j7>nd$ke^Yr2}G8f3m$ z_G6AFW(=0^MzEPGh!(+rS3xa^-^3LW+}TpupGv18*iU3Vk|D{epe1M#p)g-GS3BTVo8c!M63?&W^#Fx%$iGayp2BvrjyITzS#~Z%YQ&W$ z1ss%Z(tKxECYV&J?OYOPc_o{>6DKM&H~nVYL&S8zKBD?)L1~LNN;0O`=IVVSi#o@# z$XOUuZh8FG{9HjX?j^rr3kJ2WR}Ol0LnC~7#p{tx;O){wS`KXFFY>*CE~Fae&w7Sv zn~)sM ziJi3s`O@`UA>2`)G4w`88dHXoeFc^MV#J2Br8G0bzPr$g|0tR;!m8tf>Hg`4aePS4 zUQm(!N)Z<+OU2F6bPXm1H+p9VKWYTNd>rnKnS4*joYhUxt68x7Gv<=)h7D-B7C6@@ z81F%GhhdUg>#-8!YVU_EztUQ-F#^#7B}7{zD&?tTF{5w+*`n=LK@pzvAULHx$5CtF z=lJ&ao@$nS)o`mWuxjqO`PbZt;xp8nO|Iu~-cEdCUcT>__bbE2k;;!>pr&emnOyH;Uu85IhVw}@r%E~ge@ zbpjzc02-@|07vbkL}k0CENx9=*o>3GlWyp@#yeqI6w2&8cE+%_n-}Az!nF|ZL(voH z4iF*yuJ6UalTp+EjbN=7;$nQ>QQE2}4C&N%Yg_~sd*M>^cAY$#8-smm_!+2CG^9uw z!jc4OoP1;a#~x(W2+Mk1*lJDwxwlU0EY8xCV(`q&jG^~8#gPKe28=*^h12E*KHdve z`2mP+g*IJ#$OC4C-57UII}$o{KHR5i4FJrJQ`G<=Z+c4+Pp-`@@<-`lk=RZ-2(jw8 zfadGu)HXc)%TO+HJ<$dP zCpGO@bHZ;n3wY93hAbm^R5((&%nGo^0kE0S1a3-f6`a0xgYer;mWc_M-0*|vLffWB|FEuhtx2_o3H%jG5tbRH){>Aj6XJrH_Bs`6YCJ7@Zk#zWEqHGDnItlmfs_ClA`%=eAfGx%v!$lvmxG=7%R{^N3;0$4DY`xN59RqkoaP<$f9_v&tS=U$#3m`w6^;1^gh|Ak!*LhtjT>b>C9>s`YYq_^;;&kLif}pA^R;48UWWf?{Dh z@b0;j(hG(Y47_gkmrrI+!*2^IJgqn?hxbGkkKhIEWj4}7rg)!x21%gNd+KoL>Zs4u z3-NQ+XKd-l>~G?4wX5}=e=9}PTm~Ve_G1WM{Ujk7EcU}xPU%f}a9>hMM;Cs>)yD|W z@w`-uz-w_E+V!V(`S=wp3IiPr0#P05*WWAwfPiXM?s}A7d3e$-3OoVzH+&Vo>;fc< z67bZ8E*Ht!0GHJ2%vuD?US2tk=$LAGwt1E~?KV3dlH`(J)i@d*+}Khskd{kXQ4-pz z^(?;Xj`5vhYvWPQ@f$dd*9_2yv|bN>;TLN+JJg3N=nZXObevHJ&4qxPKfC_Vl{S(h zQv*`cZu)Oa>-9;t##*`G7xD{otO6sCO&ZPEU<+BjndR%V{kejUttP9$!oOy}3athi z`Z*vu*~i1ly!3B9Vu!_JdwZ%>cFrgKB7+*-zR|9F%Z$Yb9mi>feuH@3N7)kh&az{f zUdjB^=X{y^H;@u-ah-GnwM1*YMAn0tVO^04Hu0z*Lr;unD~ARHv)V-yS~`htTlVG6 z4{&1{`aTY}j{>lYYtM8FSQ9+R9j*F6 z8+bF$AHL!ZwyPDf#G zAz!>u3!gE6e%#eFNz2G(#A;rdKsDSEB3RM5hWG_F4gnC@MW=;@YQz1oa+z}st^@l5 zSQ82KrQt;`L{QI(@ZT+cA|?fm0#r{P-!ba+|9WyzK4ajjun?iw7aL8;%g*4z{S9`pxG zd%}5_t1VJrYnHA7piib$Xb$(5*5S?yU2^sqG}-{%k+&nYE?SKH$znjeg-)5)fqOOj z^fSe`zG?E9%5^1esa{^$YIU)mU3mn5u^ta?T`Q8BtF-}7!qxd_qHV=Yg{lHHs!fI8 zMNJ5}@7K9)dO38=r7Xj5=^yGXPgM)KT`$YpHQysQjubbY1~cQ9d)KXZe$!Z&gO^ur z>t){m?&4?_u+3_M&N%P_yuJcW??EN8Rb)xWPB3PO7Z?Uz5yJuwg{`Q~NqNjL(A8j2(-pOna zWl0Ws=TQ&FYq-Z#`_4ya){eTTQ@XUC>CY0vVYK$Wj&L2v+{uGxurJ~nDiNvrIrV)^nC22j zV0H#_EsXoKEa->CSn2`ZsKZfgx9iBU5WAnuov~{MZToIp-?yP~v5Q|;UU-TqH^1#) z0c>#iIn}6S19{c4N42@tu>^=JaQIRZ_QM9`d*&FTd`KRrWb}|gMFZF`LyanDfuVtB zSt)iO4kqJ>%s{FSokK}@L%r~Z=_-kyBiwm$$Kk>*hBF!^mMZX`ub5;|GUdo{y(d0e z(;rJHHfD{AD>kOsKh`>c!Gf-jguxPT*N-5g$6S;rD>~ZQPnYpyS?jdW>3gpiv7rNA zTS|IUN;7C@o7qKat!zQJonI0iqg&{tRA|!McJK_(r^zlQ%t%~CV&gzuW}5J%9BfLe z_esBxa0eOyU8xw`cZ_B(T(~0yBU-p4ca)Azu1+MGp(6eBErTK?jgCyG?pR7;WeCh# z1d%v!Lg`4SS(euMqDf*IEL`or~%NZNeK zzLjw#hErCkPs`=*5?SA^6Vp*UZxbNoR+d0VT;l_g?{{VT+CxC7?$H&ti?h?jlA;3I|H`OSQ4VHXDlWz2P>}^OZzn8Ktpj7wZt6 zUx%}utex!jbW&6^Z_0t3xs>ha!cqEccp(Y+Ngme+Oei4V6-s3uPG|%!aNw$>@ zShfh4bhUK*{Iw&3HovfSz4qJVyH`w8B;!7%W9J{(FLj5meu}GVv-LVLt-L)KR?xPz z#T+k26gHY39O%`y%rEGQE4}>+&Fxio{YARWgefFe!jm4V*rj~DJtexCA$b{o_z`1Zi7>!N)RZ3OCFK!&1?cJnJo$CI{TYbC1#ImOeJ zy_r(|H!+l*;z+%5wBxo`xwymYk?u`me5YLhKt}}0E#)rz?MIuC=BEtWPjtX@7qt~z zEXyY`)m0$n=VO4W_N+=|%T#_!K1{9q{Ufnhl(9^MRSOKVDC>`nGw3IzfrGbQ^&;aN zF=A00L5*qOrvd~RJQS*D+d1INZ?*_8t`llRQTGhU=r>OquMjHmqonc5?{OwP?UQXv zUD$nba=xa?+{_FuZVX;tC=0rUJ)r3xJ79&xRi+*+87H}hjx~7QSkkI>1b&H8R!KM{ zd84MHNpj(g&PYhA9>~+|QepxbQDibME;u$uFtz zcN`FOg6qlzP5nhD!0&(018HGh60*)|%<+b?jM*hpm;}VmzdrqDX`S%b3TN490l|hj z&Dm$c++DDG8Kxowz1TlYzpsog2i$3j&M8_#0M5uAGQIwi@>a$cP(+R~vlo!!N>yDd zU4gS-*=a%u>W@qB(`Vh8v(Q;@50W=K=*X!2yp#`iX$+Hf@Vd`Wny0ZhaY?9~0hvQsz2(AKG(? z$Q4_nGF*@&fND-~zz7NrTO1q0jkdZ#Q|JXmF!r8$>$8mO=5H&ZhoTxA%)~=xKfZKc zX{}}cuEas7?`z~q&XZLEx4e5zNjQ@>HV%yOXxMGVF~qw0UaEe=5W;Nr97WZY;il^E z99JF;_}a8}bf^US0sr@$y%}~#k*#+ub*H&o)*N$GHsx!&04Sd?@ZIfqayOh)Uf(EI z1D1!Luv8}8v>#R5=i;U`x&h=i(q=^=Qh4B4PLn#u|E z$kXOiz&#y_^ZwtDMEgIN@uUb#liPT367NeaR0i%nHQ)ZtVo@_422x_7Mu!R7VPE)K}*CT?1Tn_Rq z6lEe2v8EiPzDV>h8Xwx$03gnyy`7jVY&hNCQC~(4-yjIk<0uvwI zbCQKErSP)%A%c}0)eOnXS7R}$K^vukFDH~gT$~bGw5)~Y-`F8uz!JIU&7giL6x}y! zH_nFkp*crKv6ACoRrs!@*3ww4Q=vT$q7>hAk}0tbHfULkRildG)q?DPh$jU8Dx@B+ zWyk2LqP8asmvNbBc0?iEa`w6G0h!!SrTUxn177UOOLK_)*{n_f@hx6%HS4-|7+b5v zgiD>SrT0LZGR>r*wxE-)%78h=)Dr==8GmS+YwTLUXn}pPtTdR2$GS*1N$QxDR+nm> z4!c!W`hP*eXufkw0%QD)Dn@Hq+;`1Z*^`gVuMqU(9GY8~YJ51A^sC}!eA6o>VO@vH zlw~yE8YxLCuVX*@{dk8?SEnGn%-JDP(!|-pMf8o6 z(cuxR30qZGsldOJbi2vD^5O_mlsxk^O{;|70k6#6g_tY{!a;>d7dinYn!S7> zQ#wK*LJy-+8P0J|nU2(=D7MIRiJ)c*mZIw>e&rX2?HSISnXC34#Kp(5T~+wK$ydroZLnn0vG?;lctg=?b$m}4r6C6lozlJ z5%9be;6;e7CXQbx^`{_Qg5DG%7{OZRtz)LT!{a9^%Z_achleJE=`_mjvF*ry&u+w% z%HuPvI3HoE2QPb9z`|s`=|savnO1kgH#dnx>8DnlKT)1Lid1Jsb9x6$dO`2(fxZOB z$&^#QC!L06LlX?oaoPA(Y*RxjG53w?izMUXE+i!l-VTg!7&FYGX%n^ zB9cxpmPn-Ir1?An&UK`2X2L%rEW%L7NfAB}zmnkTNcO2B7NfTMXeSm(+Y5FdexoU( zJOTHS1ifSz1mVAl9AS1bEy9!Bk4~Q_$M_=JLkP@=ab#7pC!)usqR+3N!8jYAYk!VM zAe8&dR>R)}CZ&g(UU#Tvh7R0%VP&%f834Zhu&}n;Ru43weDUa(6`6~hK<R~Jg1dyZmo#PcdAyd-WnaSDmu#f3?Vbh zIgmv76;Lp0HHHFha!u1LmZ|=8R}$a;b7`T;^8d>^_Mq}?cE~_yHw0VsdQ1MDUJ=Pr z)i}G~dzV7W4h-)6XkPo1Sfm=awOGw@BtxweDOoV%Csp;OFHM8l$b>(qDQO)CEiBnjcCgJl-O@ zqDEnn_C4vefP>|=mr|7uCGOvmr=+>TZg^@{ajC?Pd`<_eoP0W(lCY$?LaLyc8AVM? zJx$D?5FId^2mhhsZkY1_I~{eO9W)|+J5t&liUUu@5v!+lzWJt<*HUP{(GtAKC`A-+ zj4$a}6NeaoFnyg`=yFQ5A^4I&ZE;u(4MP+@7|W-PPI!N@$fc7TNG5cdDq8V>@$^m6 zl{QVdu_v}|&cvK(f*sqM*tTtZ$F^-}V%xUulkZ>WoU6A{)m<08y4L&DQ`IA7!n186 zFqrIG&dj~enpjbXTKEDd<%8+p)FhCI5-)j+1z084!a*sFElM*;T?iGj#9e+`#-k-h2YY@uF zGU0^jj|2^w5P!hq*%TAB(jN6tm=9?xZSS2oQ zOG2-0e+X%?X%R?N;n&l?X@62#9rtKebRC?4@6Ka%e1#sZywh zA)CR1tB^MSt2iQuL^A9;1i4vg8|WdBQThlfK1pU-N*E2@i$rvLBrmFdSDJzUK2wqQE1nU!E z0?51qZxiVM>hri1MYEj69H|nZycm^+eOiZ;i3sFor2e5JJ&8cIboE@f?_9Vq$MdVR zE2&eLZmpIb%0@3BVG;M$Qa}>9UVB)BE@CL>EjULM@ud8-TDN@gUp0*V0GbY4{mG0U5k(ce5tjj?g~M8eUlj1o(bpl}5JHdGa@hO}Y5upW9rHZQqe~ zHNs}EykfQoXn`=AGlJ;ud`G)ms7F9N3-N!*4v3CHfmB97ZS6D_gI{B?<64g9Vq_qJ zn2Pp$^ECLQfjvu#u+|FGagu>dhX?=MYbu5S(SY#6$wKQ%An2{eKP z(U@WO!57r{UD#0Th%fcaTn8jaGN)4>4gA?Y?6+>7Ar#1eWQjyv5K{+;d}|APkODE{ zx#JU1PcG8e^;|qQ0T4OL<7#SHFwZoqShGX~&>T<*T_QeFkcPyZX3~8akZ^iCK(h%f zNVce+B55(K?;g7}4UN`(K9?T2Ua~UXYqO-5y2Sp7o$lHtqlF#C?-*Xs}+GMgFG&!9PZSkk^uC zzw^6tWlQv1n(TmdSCe1ogvuQK7sx7xqU4UB)^GR)-_$?yM z_tJkuQ%d2JgFYwm`J1LF?Y}{W7jQGP@b6U@5)%YE9OnR^w#P-u?>patukTHNHGXeu zK>I!2i2gl|Vu(9JWI9!lRN~k4vr^ulE&qas@IO7S)Ylo2A3}aJB2lLC`C+);qpb1)|EWdYi$Q;D zeDlcuTb%4#{ZAOKC`{EorMXf$qMH5srBiBdNoUW?z@NTg447Rs_E_1$+e+4V&#|C7 z@D7}=Gbp)bsNm9v5)=w~a<u@X}F{z@B>kd_+#I8+NWXm|?z_0g~wvN0A1 z$jei9%*OB*oW02ocYd{1Xpcr}xk4k-Bk%0TCXp6AV*3*cp(Mp6v;8w1?frRBYYFRvu`7_ey^*oUN|SU{n262X79*TXfrngpg6?kWT)9XMdQ(Iz`kh(j-5$$ zg$bFHU3us&JskYc$%Vi2Imk;RaG*7@Q2kaiLAtKOcevXJk=$Zw=JyGY2sr=CB{Q

zxf)%4C2!41Y0xL61TO9Z(MvHsZz9{K&ZuqAH2e#3CXsG+YHpDUQ(}d5!w7xQCxv=6 z`Qnn4`V(GEZiY^`kbd5uDRq}0{3Bi=xsYN2-s;wD7Jw^u@Y_@6q5p1SS8zS5_kJu^ znz;X8DsZ6+9j76ygpPTB~=8*K-fgy!#^l^6JI5&^~$kWb4yESevjnlHVl>!kxR zpN2cUiPx}nfpIrAjORiO^_9seIv_NEUntZ`N9~ddOd)+9MVh!O;m}LVB)U4|$qcTV z*LkAZu~-rosI7_mi- zIiULSSAVQZt2z3YUGlwWe5mNl zJvj@gx%~M9aT|e73!E~BGL>sL<<*HkenHfgOOI6Ku>S^per*%7gLrgGNc9>^zI#gM6dmJud3cD)~J5c%zrz3)LU-l z+*Kl>>KgZ8*)_mHCxch7QwJpFEyKVQ@!FSAh1^*oI zSu_7^fZX`Ugw-;JxnLJA_6`o&B*31&r$tx$-rMQI!_#TxZDH{0W})M$9H4tr&XERiDg!mXhHFw`=)LxL@xwIK&g7E)%``uM)Zda`G30Z41%C z--M_H8#`nW26f;G93CHpFCkbBl?DKwVNPi5Ex%IbVtPojTg6B?l_Gm?UUMRE?(2;9pwXy2e=OSeMgAGr%?o7|%!?s1fKg3fS4 zYkH-msx*J?Q9a4&g{fuwj<10hH#p$c@nlsn{`XTtTB2MQWNd3H)CaZJGVXi`YPX7=~goA_#of_^#!D=W6jdsB!vR z5^+xx%2|OYoy>qOA_cXVmqLTIm<-A6v4a!?KRTuEq1Q1P%5~4eo;S*M{9$F<%SfOJ zx7REVNA^SoH|G^>0&Q(V2UTS4O{e7#O1gg$?+O4fHj}T%nKZ$x1MjMC=oMv}sfX?r zzjU)osMvhsC!Ts*W|}CsMseo3>`a5?Rb$cC(LpV*P`Wm4 zljxbmS{R}Jtj|m{u)P%SM<6R?3~m>>o0A!?K%I1^CvNw}i9M?j*JgMgB-PcO}i=zbMwf8}V;&Dm6 z3b5jZ1zd@m`R2)4R-izm{#IvNO~x?DcT4JsW&!oG#9E^_vy~~GRkRTP@i1pF9bZ2nVT9{E&U>pCh7hBJcUS z%tLHZohRe8m6#PVBwFvLl%N z6KkenDuzVKyMrleeA-@~fTfUVN7EfTYvuZ;9r_5ksGLc#u@9e~>dd)2U7zL8pa>5b zgPH6Gm6%FOSo#I0{Ll?Up+O29W_(Y=tIwVxEW z*ypvQEvi!3A*O;;a&ehgUY+5Ns@7$R1)YE4V%Vs9RC}4`k!O<7IVCjS-Zra9 zskrq2V}Eu3FZM^n(~%%d_tg5z%*UQY2uzPD5Y)mwV+>4TwYydfYNk+j!n$2Mi(FtZ zJGvj=OthZ||LOlEC=h@ekV%pPe`dxOz9t5qUOj$hMap<-6r0F|bG1KSOcB-?mb|-( z)|mD$^$SUHzE5G@qpz;gtZe9;5-OztvC`oWJ!$`+W5?R56);vw`2g9>n+T=*ivoFafo_$Vcp5v%eiZ zQ!oV(Y>e4XPtE>D?e&$oqFpKgoU=cF$+u8z?9qUGL9~D0fX$a7vCMeZXW-Yetz&EC zah=&r&}46}xlKYEW$AJ8oV=j~%8ohV_Q`v*D17><0Zl|OwiWTt77QNcGe=WY&%}4l zv>MgM>tAf=vNyws70S9H-q?F5*G9qytyL3s^zDw?5Szv}A3`PY58>uOLPg#B?5^N% zcdi+}m;9`>F?-a~$xM+OeOpg(AVsz!8gWQ{cTimNAv^V{20*>i5co17xUl(#_~sm;?hR_=glYUuQ)}9+3FGt;&6hCSjovw9{AwAeW5-X zB0!GI)k{4g9p|Nr7Q}IJ=L+LKzKIt^MF6s6Sr-(7$m05WvH)!5x&Cw+r4;@Pr%bJs z;xCPDKc$F^IrE)QN~q-ME^Z>{=&7~Jx68p7mL{L;><+Aj;itE9uZ3}ot-W#pi$%-T zlj%$bq4V!ZIA@-*W}E`YQDt5%87SsaTJly8W-U#X?>_55$1IUpE^u z=2}}~xNbg=@a!W}IK9|!-BA79uDunVT`S#s#?A4nuL4-XjjP$*)r?a`L645hRUb5E zqXy0s&Ci2%MNU;k2WLk>fxF`3CVO=rqB;|o=8xjG#cY+Bq5k4_jhzSO{cJ=el@vu~ z)69qLg|UeK=7qmgsrCzs+TG{abF(#GO)WI^4IDQGxdH^;C8MW@2O``kL@)KQYgXgc z5t&ane(VN-sqZQZSbeo)ig_pFJY4aqBfA^s>6K`qvS#MSCyH_Zq$l-{ ztZJ<4kkd3*w7Nis!k2h+_5P z$4Aym_O@n>u9s=c&8#RQ8>X$Qn3s^7LhkeCSB~|+y{=v@n9&wg`HO4WLRS~+thjUX zR!Yl&3tIig@_o~k_nff3*4~V&M4a49wQ7883(y!V&~9tN{@9`TJk;CE@L$q$lqkz7 zwdZ4q9`YXkIrgNNRC+aLD6XnC9dw9SN&Hz~+!!Liqs+#36LL#m@XwMGhhEoA7u?+O zH>fpkMEI})CfX;5kJqU@K0L?oaWUpc89H5U1S&RkLp2*Vs+i&La(ci$Rp&NOWVObH zImw{Ws2qI9E#tlTKBVDdZE1CcwTS$eO6NH@PS9OKRVJN>v$Y~`XZL|Ati67P%0=%* z$%o+t2P*}e+K?2$Cg*d%_LB{fSO`&w%hFZMUElHJ$!nvWjb{gb!;^i5YVGMEe!Rmv z&V1Tm*kHchcr}k`3TeFx0iJ$mEzz=~Jn@L7kfWK}61rV8X>+YfLX-T3zh^hK3fWSa zT6g((#rN|@x*ijgdO7zdb4Xz}+H9){&GpSZnM7E$4}J>Stl8C{S{=#ROxEq?e;*${ zp6fU;f*qGSK4tD?WnK$D7`aCJ|EG-RD_0h0NzxA~1bCx+; z3Ti(oShCp6NG7jpDPdqTPdZd{p_%n%Nc{(F>zxx3&}(Nb7SKYwvBITy9*J{1OMi0KdA9*oT$jIc0gQ zOSqKt>qjZ2H4~eZhrN7~wKm@B&J$V5`qxFSY%PMQQ(73k?5mdBVKhx2H{MET_+Ob` zIu(p7Yt~#X>Q7jGRF<77~@K3AB$yfg-yg6}7%r1cvI|X{o8k2&bmF)|Y)jbapb?$7m|i zx{fv`($G}?-CgtA+%B1ol5ukkUo!BzP{TH+yQIxMQ&_fiKQ)G{`0jg#saF@jph^6>%Sqwm>gsguU8num91Mfja0!9Y(` z`Pd`;wZ{d&>bB0NMYYwAy_+jDKR;(uHRG!LnV|P7K-yxPZ6Yn$WoqV2V$2$48Du9nR9k`?veoDOxyLnX>NRyQY5ayUX}B(U(!xcKh|Na#+s~B1XGt z&Q-nRF$wJL4eBE)ujn<7%pGP>@QzOh3`1vV^r+I9UUU-@YS)eMA-L+93=gn9*Dj5! zN_R^}2Huo(F<~GT$Hio}_9=z;HabC!Gx_Mx8}fL){W`H{D9+$ZHB8J?JVn_YDJ{^c zejLxr$C1`w_n$Edcg|os&j)h`b`#}XB^-YO*t0~RHBOulpDE(<>dB{}Dbu**hwqAg z-ct^K=IG{oQF&kFha$*rLlLUrqRP_Ea)JtXjL@`(=Y8zyI%+4Qrh*|7Z6zVob;>17 z4+GQV|E5&)biNYbaKl;xI<{jiit4K-U94Xg{BD>#F1Pp|z8Xw!4HpHp@Xy3DQ(}O_ zijSh+ne!1bDPvWE&Y61?8g?t@e2Y>kn10R0qxNXW$omuVqV_+I*N>g(-u0 zQ*g7Hw?_|qBC%?h`;S%%XHhy&KRNfWy9)MQx3bJNtPa&YpNG3EDD!H5&Ic(=?wxb` zz3yg;j<7IUtsrMz&mD>*guCvk+h-UG2aSb4Vxl{cR@}D|<8jxpd_7~43Gylq;K^*_ zJO@mgI_d|56ah`ky1^mR!WHSE=;?W63aY#Ov)2Y0A0W1(bcYTIq+cBt@U&T1wfk&7 z>NT)W1E|GJK{qSzqKQtXl;z_#$73J`#IGx#u8}__{7QG-HAK>i4nM)-M62s$(Iew(HwMV?ZWD%AbV8azw&n4>PRUq zV{;jtkCy*4JAA!eNnK}?4`+DYOtG^JosmcvP#=MIY3%9Z)sFuZBY8JCwm{d2 zRf$?0HuTowv0Rd&(~VnQS?KLO@NgQ1SLIoXa7N_ya-YVBTI|uf|KK`Qs$R&6=VxYDLL~H?`E|UfmhYjYY$vCLXJgUs~ipx%{x)0lZ6ue zyb;Q7&KNt^_9A3hmd+v?4slIvoT+G|1G^FUZ4N_J#b za>(AIZ@IP(KCZd^VIs7QHDk2u>)_wwJ3DW6xgqW^PK_){f67jN>R~XRD_;A*mpPuV z&(9t1m^LX`5vL3A9#vhc8i0$32lHO-?)6)j(}@d9CKeT=4q+2`YeVu;G-i^nxQ2&6 zMrc`>W31k~UotrpngSnutOKp|glg=ZB;9b{ zV0~?xhu_ZkQ4|acG}LPmcJMPaoD{NpYzS{5$&vYaXh+i13hC7+GT~GEY~2(#b8!an zk;9z){l^Hk<;GA_*}S`FX#ZYBw1GYbO=0F_%Y#kO<2J~tW5gNB)~czp49{la!*;e_gUvOnZhtN+%&mvBng)~{K9ADmLv zfW?OSSi)f{)y&NsfNGg6TqYb(<`&x(qsP=<-+k@}E05AvWcg~2?E>K_)8NiUea^on z27Tfsoor3XVt#H`osc~hT^<3>YHdPUc*~oeJW76hrp$7In1}Rfxeya77Y-(ykn$BG zK`ngzl+B@p{s4z851h(X^P?o5(x|nRRZ-+#AgftJT?QA734K94Rqk z4ljJ6I>v;4#~0Gb-YM1DZg+2y0jEOFR#OHY3?n z5aMK@scqO+^Yc?k&bJ7t%y|mCNJa~KN3aNMbGGVQ_=QRZyC_2UtPPv&WI&!eRUC3& zGl1VfV?-0$e3~#rz47CauWBxCIec`hQAd+#g1cOR1F^W0EE6MAfA7Bb&ar-1k}7KK zaYxTtlgVkQ@F>OK-ri-wRfjOe+{Nm%O)GQCLbkJCp`B{|UROZnJiGVzsjimHOWnKO zI|~6}4UVtS)VbJER5hesoKJom2oyg+3aTe4Wk^X*Sg4Mdql;MnJgw8c2i z#Y=^|Q)?jNhhB~6Gxo#^K)9sGLm$$sW_x|+3t4@J%bgz>Y8WhbOi-N~| z$zyzG&==v%I;icAgP)n!u!+KtRGN0V^~dWf z4=13naF5bT&<^`)zq?DKURotC@>6^4s)bq?45I>NkEas&t}qtInne^gS@=PeiGSIk zl9b$w<1@Xa%T(r>U2P>kK}P2M^N8W>UgV#Ho!xB>^Q^ms)n31wE>3>SE$x3%PjTF? zJRRO>A=;25lG1bs6%}I1GAMb+bxCHW?Rn6v&lZ})MS!G08t#(d}ahr7g z{Q>1?<+o1SuAP{ajm<&1_EU}Jy>BCL4Ual>ycomlg0&yc)P%fGHpA6Sl`{`7YaQNs z5gs^k-xhmfy?TxTF1N~X+vh0hvJ@&){E(RI{% zko!3Rfrq=`E7yIjc6B(&RkOBH&~s@1d;U1DshDn%{P>Ro>^1U&Zz_tQ^;5jsvTsez z@#?iTT^Lmv)PSGpaNtvKpn~i&6{w%y2+QtAQbjSwstPz5B-YyIJn;-UP-7tnfd*@h z-`r(#P20G%R8ch1kS+LFluVhK0N4y; zhMtXU#zlqHPlgX;^ z3$dH1*D%*2){OllaEnRci~4K{K@i3zgxm3>mxoS1(`Gsk3Od>q(9cD6e_8)zzP?=3 zV)V%J^>8E+fX}M&snr*lfiZ$!mYx?`u?541*z@(UwVF{`K^`7;v;5f6o^O(R*sVmE zA3L#RW{eT*&!tq-xMEX5TV`?bdghcft}7B)|521u%ydOn*>_{>T`@YEyJ#6{GrSNY zFxB^<4r;AQCfW4iiz>Uf zx|9*P2jAzu30(7g)C#dhaIlCv__Gvr{Zas|8pHsGHKPI^^|m3Y4EEW8Z6=QMDeN<3 zAP&ZGO?Kv19qci*2#)w@!s~rv&&+qReFToZ6D_&<+>&Eh(oy%{%sa-t_iGQJ22BYX z9lpo8yHGyE65M>mdbVg*ZRLh&cYl6&Y#*-7^8^TB9a;T?D02}DfXJSMfx)MpEV+^# z(pqrZo^UcNV(wQR0g*THhsi@h7Hom#AAF@I?q$Ud@RiHmWrUfaP}ex6*&kyEBZcGu z9=C0Dwn-;nPQ$>UuSe!x4r=E~#r^r6x^0a5Ly-FCd*{2hEXcVfL<=^Do9Qx+I1s(mTN6}}kWXhgI2T-oeO+b$=Gmtm5Lh<4zF&S4{OZjVMxI0`Crc`*Rjl|5GN^8*oo)YSS9iT#n3Zmq?gZ@vC7T&hgu_ze}{RaMq zL-m`>y-4A}6R}F9OE+8KP{iJJQRVP1I)l;3M78$WzNn8EP;|nsO z7qaFTm{3p|@OIwtxvaFpW$3r{A|B} z%x(gJ56#Hr6erPYzlaTO(LSpn2a%0o`{s>*Pj4T-5`b#MfZtk~pdi!DYyBcV&Aa|m zePZh@r5|bXDWsHL6|=@umG|{pa902w40&M<>V2g5gBV3uZsG4a3|W^4@Nv1%0<}Bo z9!z6a@7{nq#(tU4mUSN4FtZyb_6;%395_nGgW;iCD0KK!PZQ`Hu>_GDqS+Xty~`Ve z!_gZ;*d$>TB6B;jZJ&#Uxh~7h6<{5btqH!WoQ#iaDQP>0mzHs7oyqKj^(r+d(u*s` zdylURFz*ITE^9U?pSEwazua#W`EeW8cDCs!W5+K@|0VyUT;+EnlzKIE4&UeE$f$r{ zU|`Ux%qX@yq&MZSfV1Or?SN+&rrOe$`qjVgz-t-I^l2{Ine%!(P3uyGRGYhH2t*2) zxh~>oK)1Pdc~$&{&+{?1{);B@D>l+Eo}3k(prEq3dxGz@_`|fnS+}2pPddd>0+lg* zamG5|QRl+xvQ6XY0k$^QkUQ^~m_cV5OF2>?IZ_OrAiD|cg>_y=rJso)h6`cl;)S2J zG__?juzNQ|zWK3o@n7O$m8|zn`py3Cz`}aG;Xs}#Vxu~yG;=HoPBnF#aJd9}LZiyw z!4I1yi0&)>1dhJ!f{$iLUJw=$9U-HvsI+f(k#OId&ewjP(WyzfPVsX&a)=D(hznzJWnso#A8oF~q4BZ_V?S zo)jtRIW>_>e(*7VDQZkdj&N_?KtwnuYL#XPHiBX@r~Hhd0mWbPopyRVV4hh)S_i#^ zl&l;NE#S{;_-5SCn_|I0b)j7oQE>kQJ>cRPstcv-SHJt*Y&xrcG!!_lQJ5lH;MdH` zaZW6LrbU>5z68FsY3g%5%*BCa7(6P(B_;Um3Za;FXbcX&0nwB=LT`<;IO<>HfaPH{ z)IGgfJAyq0t1z*DpU!z2QAp;T2TF}T*69N&FfwIHd1RLhH58j7wh9r>)nzwyYM71; z&V+*z3SyBLKbt8wMW>Z5Y_`-N^J+VsOS!aU=S?oT!kVcxbWwd_HOx$;U$As?ix5{( zugH!Il_wP=mIYp*V}Ahjtlr3B&nX{AlO7)r^&4F+UxDR|=IV~SST?_`j&mi%hmH>` z>kr2Y*4)=ur#g$)j1G;nrRY0mO)aMU3Gy<=A+qAnpG=$9WaWRMIijR_Ir{6x z-X3Nc94~2K3a)S8Ln^vfQJ0b5x80ZeFfr)jpH;5|O2zs?+QT7yAeffns0IY+@o&QN zt{kOz!`S<^6`p!0Apic{jL-*D- zNi|^bl)8neP1E4NRo;zO>kh5dsPaM1x%$!MBXXoA>p^SPq4L1M!apO~QE=X7)4=;N z#lQFWiRf=B%~KL=*HOTFQU;MCAGVmhP4%@&_5R}G5K(Wmvx$Q}BKt+@16TEVWF_l% zGmh8J=ml#H<@U-XrUv#m@ZIz_(h)Go)^?({1V{V7apdD(*U`^DzN5w1U0QJ`fuW(X zLD%&g)Hk7ZYDTeiX$@CA@&!W!|pLt&}YKq)lsVvUu#gx{FRq zO0=}Vx6jR?Rq+(743*Ze!1-qRIZ1RT;63?|)Bd)pRUpA@7h{y!>lQOg#E{sfT~*}Y zr-aH7bi-Y_^RZ4QpTUr3P{Gq|xf6S=NBB`Vc_c!wV zx$bgI8*MozA^{ve`crgp7VAoeJLK6ESUz0D{}G^lPQ?S)CbhB(B}6TEJ*qL`kP%8g z%1Ci7fvH9~MJ2HizE5D?NX>GQe-P8oQ?&}!iRYJSB`cnen5BSQ2tFoubA1C|DY~+j z1C+vk%y1}JuNOa6gtC#N)F0#G)YPE&|6ZghD+)HnHA@IQn1^8j)y^|93v~{V0cIW* z@*sNZjQx%zoTETZ3O?pDh5EGQm4(UMQ|nIL4mK62S-u!yds+hQ-Q|yZEl1M;&XEqy9h{lh_t_q;TBjoX+S0%mP^|@f3ByL@KBS3j!D+ zIkbxEKT#sY^~4>iYFwA#b!vu%{Dt$#a>tW`ko$#aX+p$OkSBXf`(b<9q_&msNzjE| ze&%<$2oz-<)nS=?GFSN<>3(ACATJudR_BTKo?`G=ZCX23kODsmU9DiX!VZIiH!K#E zE9gO4W~QevoMvB?igh(872bCAB*TCLaJn&e&cW|{&q0=RZ&30&IZ0Oox zQ2?gqo5xnv$G*s-|1MJC_5>dr+a6oQKG!GI)HsGA)R1uFrC%n>_)}1pu#Vrz045b=*KQ@g9@t`NzU`Vmg#R=IMH0q+HQ|=HDjrxPtrxp^#WP z)!@iu%9!7@GELDEE1z*DkR6dciLnd|X8tF+e9(4h^Tp>F?4i~%>dQ$5ao4b;6tC1J zLmcHK+~FI64K}A^-Dk+;iW{*1a-X5R&f;^j0e_u%*2=~0R%;o`DJGgZn~E>4RPuxhvMh{sN$84{U( z%#-m(8>*AcDb`*TVQL3r!Gt98A*!m+SWY7v$e7rg@DJi&?Uf{9SV2u@ylV2xGzq

)jm!1@vRWAr?f)`l5igo-<`6h4kx!^b~^4{mFp`pTY4TVKJ_Mpw`r3 zi^8BFf$*l=$^#D-5F_^i-a!8EEHi>!cT0kq{Oe;G7%?=ZgZ0FmiGPJ<# zS2I>*8Ds?u;)s5TR)0BS=*I8Uw1?zkA^yfpM;1icZ7V@>9eQ`C7GT|I8tPrJ1>Pr;QG@)w{6he+i|c>tbTP+Wv&$c z4I;C5NE=tF7vkKUSDjkCz12U%V=6z7@cHY`_GTv6R58vl18iv4YHiLBV#5*O-}ods z3Mmw}i6KNI8BRN=4Ecmrw_vBp4T)tpZAFu8z@_jEzUKGx?l#>KsLe)mNrFR2;?}4~ zBKk;=9^x|}6J$RX6KpY(_s$L`dF#}RvSW@f5otI_0Jjo|VF5(7ygj2RN6 zDB{bAD%igwn$d&aP}yNX84vy@U(=CRSW@nVaPr93Y4(p191wx#7Kk56 zJWfLnDKDmU#7B>9~k5VwJrzlQ~-{A7m{ zh(0`fN8FDexvA{J-C%oQf4W4;l$NjT`6?E(Ql$>z`NtNCizO3j%PV!sr6#2K0&^T) z%n80rkhZLh0raG&TguN-qyv5&GQ{U3g3%=EM_Qag=A%wzU`vFo9C^t~zT_ zOdMR5;su5}k}~z?yoa6byTRVWd^RbrmJbqR#t}#B*)I9B2~9{$0f@z>n*l0og7^98 zr1_E{KOiNUKLwX$ge@k?nf_ZURb6lmRf*NR*0;@H1KF zmwXXgUb^Mgr2Ubo{C#1RqLVsixVuU5hW>j0<`1H3jea zPZ)JDX*81iq4oZDR*lGM86x4FbjOPyI+@V&={MNf& zGDBOXE|X^m^08&B?h-g)*VEvq8!?Z(gn~WED1^cWsn6m$w{5|4pRfY)eYc zU+_0Q80uKL#@NcEiR)&Alx@zeK&hdN>turz<#>ttGNoL;lnT49q)}@dWHy4Dxc+7A z6!uw3Bi=@TA%t#BzS>;0eW0RlfMfA!e-M1np-SIwk%BgjNDkIoM0~~mk2~~`jw9V6 z>inOmoC>)?Ymwe1BEp0^NsVe=vISh(r8M&sSu)RFu6$n9Hw&i4y7y~jOdzgN8JR=R(rW?|eA4*aE1JtO|W7&(e88zj||Mz>2A2^e2kXcZ{UJzZI+`p`d*ctJ3kqVJ8S&}wRjSbQ(p;?cU zp?qn$g0#0dfa}a<1GPws21T6>5>Ri{$gfRNC#dBE4>@qtezz~@+XD5 zD&4})iqf?kjISpq9hv4>9g(9nz#tURwSn$#K6o zdO5ERQRz_!;80sov?R{t^N-9(2(0vo^>ZJ3PNy0JqnE3nM@5pV9 zNA5wyY#sl!Y_hm;R>+y#z4Vpvy^o#cPSa;}8B$U+xJM(NZSCMMJ?eD*xN!j zbu=F-DtaA}$u>s7w$wEX!yl)iqU6hzp+NR9Q@S2jQgKHf>E3nx z7K^@3c81rq)GvpW509sh>@5p1=!@qo0fXdg8Uw?M4vsZX(bEm#;=WKjFy)H|HT@nWzb>Rz`&U3E6>Wn5M ztUaIQT((|@7rZxdL(;%G{^3l8VfkQ&@y1CDNDjOgNDmV0<@D2qP&J>3JtSr~l=j&N zuGlQF${HrZvW?#6FBkrTq&(=MbR*ZIWu`{rm#X@Dq;pXd#w~NbgA3(ga{P-ysqM$X zlxAEJ2NqG4i`n^c-KdIxF)P)CZ6Q9t5ML$TrV9aKTL3twLx7j zUix73_*z$T*9g-8Kc#(jR2<9J?;wLu1Tt8H1W9lY9!Ox2Ai*Ut!QI{6AxMCrLBiln zf`&lQ8Ju825(w@t1A`CpxaZt+bI)1dAMbmwdv))w)oX33U2E5`t7}&obsKo3ZKJ*5 z=V7GGv{LvO&-W_?VT$gpY$Aq6kB8cX%9dXso5hJ)<^tkoGGI%<;{$C{Z^ytl5{jHT z-`FbixvOI}e&l0g&WVn^O)@uH{dM*~lT?b?}H+9G)vMpRvA+EbWs6c}X5nFUit2X<|I5g^$TMB1RwMN80yrBnKW=ZU*?YN6$|0KuD_YXL%|VxVOT85 zDpJ2ivl}5A0FN+(wedjAxN|ZS`&hkg=E-!5^gUlRvA*hfe(C@tB1Req_u_3D-1!e^sH;P+}!OPM?c8) zdXVK(UBi^((X7gjx;Z7~v1bHp6(I>XCVAzY?IX5WRJN8YSkRbF6&Vw;57&Ogi-7`l zrhyQeva@5jr0M(@D4Vwp>VB&*NoqfbV)QGcH7&&_coZ8V=p+i}J*JC>A!?bXvqT#( zX}n2;*;;%j9*35dpH_h{FrEkjE~SHEo9FFu6 zTOT#<2|yP@B2@6os_%fGKF}@c5Z0u0_Bz+>Ak(S$T11EggFC@omGc|u;9#PWHFk*I zF!AD{Cv2c)`sC#Lob5pa-qM{@Hn#+6tpu7(nM!C#*;KfRV*>bUu(faS`VkD!6JCq^YeFMev?O)_Xl7JoaF+1|-* z>hAB0#RfD);HZiybrJ9Arvdh`$vrhqZLPttvd?N86c(NoeQsuG8N%20)4%mjH)35} zO*^Mv7FF>ap);;r52-+RVhLDOC-*-$Y=P#=S!0i|CL%%k>Ci(s=bO z8aBRL{N-T{SB>5{ii!THS>6O0*0!b`2Fx8?jjFH2T8+emJQ91mQooKv$l_F{8dd9B zQE-RtT5`(#`D_9d+9aU&M~FjCXkC5}KSjc$0MQKkPw27|vGu5~Xczgl{JC_hq-Z{A zWJ@jBi>V-UXVhtx57pGmfuj-~joS6(bH+IsZYuI0)fzj>2zR2mk3YKMdziPrkBpS# z)XBO3m<-4WQaXLz{;k;xdnONuIKA%T{?;8p!7Ia`FuCY`s3z93uHQ@ zf~*Y*&QkxCcPZhgnsnneOn?0sEmLYrg%&RNp%5(do*=Im6!@Qu6bgZQyv&RwgR$yWCW~ca-sp|{FCtXPEmOXp8P1&H4pVa|EVIv~b z$W3_rHOl8{vKO`w`7 zGbNvgc7xxh-D%Eakep2{u4z|rV(w^R3$--z)Y-_YK*B~4sp@k2>?C?-cdo<-W(4)F zJAAtL`Q8so7o)IWuP8Y}g$X=W+~}b2((fw>r_sfR{HDXQF0Ruf(Yihr->?z#yiO%G zqih`00StUv+nmZzVm7NX@?>@hzR4wsPZxOE+cG!jc;UKg?4We~iP=Uy@d2fRglOb! zF>!pTJSO)V{idix>D0WjAwb!If>KDmE$hTtqnO3;8)HxTx8$$ll&moDjDZzqWYSXc z#|KARiPR{cd=?Ae><4diZERv8l{HB#jl{81H&xcSCC^aJBC``cjR-{NxH~XN0lzW& zK2TdGL>tj>#99f@IQv|z=ie;7$`U`wcs>DEX`lZYyl*Di%YKE;NNl8fR+dLIEzeW$ z_d2lwAe)jMH|w}A&@aNqcQ2OXQUsKSCAaCnd*-|(pn9a5h!+1*zl+@zE>?i{6qVPH zQ{f15Hb?jkCf!Eq$fU9%GN5#v=8X<~G8m$WWVRU;LtdHKAohb7$FId(F@o6B4{^p# zf|8l&$e5*|KDTB`arg)?zArGYE?lUj5V88u?k|HC3)Z7DPOhcm?$Jn8(+nb%Q^fo~ zWjj63AKk+>(G%<9Ly;ramfF0#Fx-<~I#yzrT=aNL3~@M)xPH%=vedF~eeEfs@(xKS zCKMz-M}bZYsDzE7D!x`KSM#8bk5t8D+brrJ3ya{IVK$oP%SAX2q(h3xy7_5fU^1JuEo1X5AhtDWO-lPfJINNN_MM|_pfYl;03cy zqe?b9Ce#rM?80hL(2%gd0s<7)-NO+{Bo!_Xpm-<07eb|-aGH`P`9`xR4P}W>hnrMw z$S}K8(Jxr3!CIY$T$|*MA!!#Ob2m68-Io1Uo2Srtr!JMOLBZJ|PM2Tk7W6{F6a6$) zuguqHUP{fzNGz(j1Fh-6J%Q7DX%3M#D1UQLLc*y>z=&?GY+Bbzb_&&FBjfL0gj`b? zD{0iSqL6ah!U(L^S$$J#=V8n+r|M?34e!0lh2uL>Ps_R)BkUh%r3y{SBxrSC4yCp( z(X#Fd3MmT-fXM57`P{+=pHC#Ey2(Iklg8gm^fb8K7bm%RYWY-je1ez6R7UKLYM`m{ zd6pONQLUAXG`WvO(L;AjH;Dg})DTa7T|Fh(6VR*U{+npYQO<%B7>^C+G*G8 zo`k5=eE&!G#=b(SQG{B}&be9xoG32zq@r3?5gdXrU2WFNq~WqsnAS9<^P|_F=l^WP z%Z9mTu|V1+eX(XOb!;CVsG4AZ50S{=-Z-b(7@HutZId2n_<$RVtQv66f;CK_h~n*^ zMp&7-I0b&Wd^#697QenbldfO3EOn*6a!bQ`Ksk}F zN$eN_Sib;-Xpp=5o`e9$B|LZbxSa&IRU0+?>~z|8sc9!(mR9S~Vnx-UJ{?k#A^6Hl zI_;&E@NKWtt489=`OBa>P#eAQ9^+T50r-!&R_&T)tNLaw~O2 z1m*i!f|K^Mys=jsvf@*RTqs=6m3i+n>li<@)4uN~cZcGx6Y?`sZtyA0l`qcvBWH#& zNMfixyFveiKE?Yc8@cgCCDAbl{v7|g>5A|rsXuAMDEE{EWDuBUYm0j|md$K@{)Xz) zc2Wi%3o;A4Yq)s|7{$48ADhn362ZLL;vijqWp&?#L5jEj;S+wOXacz)3h3u%6Bi8Ib*%-uluCqx{%m}y+$~Aa``Z{UR3PZs$G$?b&}VO zmeU@ujwGW|r{}w#c^fs3%D|%$0xzj}dIL_&@pplZ+Z#<*M(khtor`Gd=kCiLR=dAez6h7vo2PuH6JuDPG`%H_% z8MKscre|x-UTabQO5*QqUVw)OKF9{k!kMCJuc+-dBWA7Ie%ZKZ%I!##AJsdS=Kxe@DdRL-YJ)WsJxeZ;i9&T8@nb@->s*mObORsrN`yTL2!zGYreaE_H`{`+2R zgL@7*11+?kg2(sf+nm;8_3dLK`l?>@zwYVigKFJJu@LxBDyJX1Jo3hg3O3spS&T-X zy|`0a1li@GSEkZdIQd%3ph(Nf&XDbvb8?5#Kv=B$F2bcZBFI^?@b2vgB|k=N z5Z#eOxq2$?R6M27Sx2*Fglg$FZJzj$;@N9fFjm=w2h{+|XfcxGp>{_h!?YzzQ|8(b z_*{+$MrrRzQ~y4y_>R)3z?D0bE_;e%N~ZVLk;Tq;%-Bh|n2p*V!Li7bC$VB?uGkrWrB}M|tx358%k-ktG2=InCsWLQNj)W!Ai35~~)k9-Kik zV2ScL&y!5QC(o92_cB-<0$!^=F;f=((UPRIPSrZhL$~9m^h@^jM$mlZO^z_n@p3wZ z_vI6v#$iQLMd{+cR7khYGp`RXjG8{|-K08b>nt35HZM3x?KcHDY;x;Hz}{@l?3LqS zH5A23Ec;itxHv7yYV$5=8_p33o=4sQ#hlMrC<^D6g8a}^?vOGMTJkL3$1!P42*}(kk zMgM4E-gAGaD;WbWQ-XJb_Ods`jJy;w6JK2FQ?XK=aSua2LlvE;4fuEn)uZH9A0l)& zzb1@Wt`|nq9z638=VHBzE|51b_`-kGnM#h>U2HeefnFVT=U$GpPe~a@^_8~csb4}8 z?-*z1YgpW3QlYHX*f@gkwQjXGw#f0M?5DB6#252Hq;55@}%WBDm1Bjd6MMu8oz)x*n>h z*^cd3?@Tk=DVTsy9H|CdUAl9{s-R5ub5K9+DPf_7q3zITb!7lWkW zcS1}L!DyK#sb+X>qh;kb^%PH_8)IHosC)XyyEI>f!M4Q9Y|hF_@<^h*`^1%pA(Rs} z9hCf|?T>og(w(X-CW)Uyhp1hw`|T9EyR$PY5|?9V&OC3`A;BA(2*FxTD)}LrY%??ZW@d7wE2*N6!{$lQ z_0rDgqR-fB{p)x*vJ40fLS>Rrvb(D3BZfwmwF_%vI8#4Y-`R(`{N(+Ls0sKuY1xMk zaEwdy^5=)grAOTcORTyD3mW!5`hdMAw;kK2oe8R!LJqsw@WJq7?7qn=)rjBv9x^lO zl|p)x2lMOcdE@kfaQ6qd)?SA``FCn1KNxW(D=5fUCLbshjuM8RD(jcG2{W<-$T$i~ ze)=KFeU7D*CDXpsd=b~3QzasVcX&)cIYS%o7a?WqU9sTB^dA=oAfFEtPl>zOF zj;_xN@4(ReU!@5h;2wsoN~__nlz~gf4>h&))0QDe?8MS)O=ee*+z9=hO6l+a0qhS)RAoRZi@9qLfpq6QbQ>Qf zzx(YSy~#umU+kJ4>M66+0KqhVS-XFjp{S_OF&ZIylcQc|yF#4r^f`hQ zW6(QnU$~FUhoh|FECO}p;={zVFVUCV#&2ROTB8MizT<)Y;(}2P`T9JcjHYjSNaAb! z44EwxuFC#M=B?PPo(*?k)E3s_HS2;yOZA$qPg{9oW2R*Dy7)|rlqd60*wRLTHJWvJ zyEAcJeU#}1rK};NM>B_5iqkGHnQ?-?VIX%ytHe33-lBhuo{~|ZRdmjEyx!A*bctL6 z1~X*$DSmy@3b1{Bt0MO)k&0nQAR8tvWSeezMF>lQ){Qj1Gr@Xe!DZT4yhu5QcC@8a z9w%0SEl%JT{|GMQfCA${d+cX~JbXm{xyG7)>}RBWSV5Io`BqueM`7J+pmW{s-Iby(+Z{n)_R8ZM6>3;9BGEY1$3={65ES zQCWP;hOmKJp91w<*icrmnOCaTGZ!iT`AA{Mc_j77=j-|v7eQ}a0$M1zrDueebh)^(k4 z94cSHHYGlDT!R7TOdHMQBQJ&UE*mv}K&QvPh%fhNf7E;;QdCw+Nh__Tdk~+;#$ng3 z3QnysEg^a{#;X}#xK3Ht7UD{xW&BD+AxgG0|4{kauzAEtRd?ZD-&yQ%8rt!0vQRcK zRIO{XZ^N_jeO;Lg()LHo#qRmlk2zIEENo7|pOD+W*j?$XuFz+OBmlr70UvPdrfc}~ zp#L|ub?`E@H+FS1cll?xP-(bKqks3DW=O?{Xvc{{`UxmheAA z=YOkyL+q0OlJI{Cq<>eV)H?Qh2aOJMz5`9IY#eBjo=J45%ss@&3gX@8;CL`SMuG{#WV$0Nx(k+yDRo literal 0 HcmV?d00001 diff --git a/examples/knx-esp-bme680/bme680.xml b/examples/knx-esp-bme680/bme680.xml new file mode 100644 index 0000000..5239f18 --- /dev/null +++ b/examples/knx-esp-bme680/bme680.xml @@ -0,0 +1,98 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/knx-esp-bme680/knx-bme680.ino b/examples/knx-esp-bme680/knx-bme680.ino new file mode 100644 index 0000000..1eee699 --- /dev/null +++ b/examples/knx-esp-bme680/knx-bme680.ino @@ -0,0 +1,257 @@ +#include +#include + +// declare array of all groupobjects with their sizes in byte +GroupObject groupObjects[] +{ + GroupObject(2), + GroupObject(2), + GroupObject(2), + GroupObject(2), + GroupObject(2), + GroupObject(1), + GroupObject(2), + GroupObject(2), + GroupObject(1), + GroupObject(2) +} +; + +// create named references for easy access to group objects +GroupObject& goRawTemperature = groupObjects[0]; +GroupObject& goPressure = groupObjects[1]; +GroupObject& goRawHumidity = groupObjects[2]; +GroupObject& goGasResistance = groupObjects[3]; +GroupObject& goIaqEstimate = groupObjects[4]; +GroupObject& goIaqAccurace = groupObjects[5]; +GroupObject& goTemperature = groupObjects[6]; +GroupObject& goHumidity = groupObjects[7]; +GroupObject& goTriggerSample = groupObjects[8]; +GroupObject& goCo2Ppm = groupObjects[9]; + +#define STATE_SAVE_PERIOD UINT32_C(360 * 60 * 1000) // 360 minutes - 4 times a day + +// Helper functions declarations +void checkIaqSensorStatus(void); +void errLeds(void); +uint8_t* saveBme680State(uint8_t* buffer); +uint8_t* loadBme680State(uint8_t* buffer); +void triggerCallback(GroupObject& go); +void updateState(); + +///const uint8_t bsec_config_iaq[304] = +// { 0, 6, 4, 1, 61, 0, 0, 0, 0, 0, 0, 0, 24, 1, 0, 0, 40, 0, 1, 0, 137, 65, 0, 63, 0, 0, 64, 63, 205, 204, 76, 62, 0, 0, 225, 68, 0, 192, 168, 71, 0, 0, 0, 0, 0, 80, 10, 90, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 2, 0, 0, 244, 1, 225, 0, 25, 10, 144, 1, 0, 0, 112, 65, 0, 0, 0, 63, 16, 0, 3, 0, 10, 215, 163, 60, 10, 215, 35, 59, 10, 215, 35, 59, 9, 0, 5, 0, 0, 0, 0, 0, 1, 51, 0, 9, 0, 10, 215, 163, 59, 205, 204, 204, 61, 225, 122, 148, 62, 41, 92, 15, 61, 0, 0, 0, 63, 0, 0, 0, 63, 154, 153, 89, 63, 154, 153, 25, 62, 1, 1, 0, 0, 128, 63, 6, 236, 81, 184, 61, 51, 51, 131, 64, 12, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 0, 254, 0, 2, 1, 5, 48, 117, 100, 0, 44, 1, 151, 7, 132, 3, 197, 0, 144, 1, 64, 1, 64, 1, 48, 117, 48, 117, 48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117, 48, 117, 100, 0, 100, 0, 48, 117, 100, 0, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117, 100, 0, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 44, 1, 0, 0, 0, 0, 98, 149, 0, 0 }; +const uint8_t bsec_config_iaq[304] = + { 0, 6, 4, 1, 61, 0, 0, 0, 0, 0, 0, 0, 24, 1, 0, 0, 40, 0, 1, 0, 137, 65, 0, 63, 0, 0, 64, 63, 205, 204, 76, 62, 0, 0, 225, 68, 0, 168, 19, 73, 0, 0, 0, 0, 0, 80, 10, 90, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 2, 0, 0, 244, 1, 225, 0, 25, 10, 144, 1, 0, 0, 112, 65, 0, 0, 0, 63, 16, 0, 3, 0, 10, 215, 163, 60, 10, 215, 35, 59, 10, 215, 35, 59, 9, 0, 5, 0, 0, 0, 0, 0, 1, 51, 0, 9, 0, 10, 215, 163, 59, 205, 204, 204, 61, 225, 122, 148, 62, 41, 92, 15, 61, 0, 0, 0, 63, 0, 0, 0, 63, 154, 153, 89, 63, 154, 153, 25, 62, 1, 1, 0, 0, 128, 63, 6, 236, 81, 184, 61, 51, 51, 131, 64, 12, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 131, 0, 254, 0, 2, 1, 5, 48, 117, 100, 0, 44, 1, 151, 7, 132, 3, 197, 0, 144, 1, 64, 1, 64, 1, 48, 117, 48, 117, 48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117, 48, 117, 100, 0, 100, 0, 48, 117, 100, 0, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117, 48, 117, 100, 0, 100, 0, 100, 0, 48, 117, 48, 117, 100, 0, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 44, 1, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 44, 1, 0, 0, 0, 0, 253, 222, 0, 0 }; + + +// Create an object of the class Bsec +Bsec iaqSensor; +uint16_t stateUpdateCounter = 0; +uint8_t sendCounter = 0; +uint32_t cyclSend = 0; +bool trigger = false; + +// Entry point for the example +void setup(void) +{ + Serial.begin(115200); + delay(5000); + Serial.println("start"); + // register group objects + knx.registerGroupObjects(groupObjects, 10); + + // read adress table, association table, groupobject table and parameters from eeprom + knx.readMemory(); + + // register callback for reset GO + goTriggerSample.updateHandler = triggerCallback; + + + iaqSensor.begin(BME680_I2C_ADDR_SECONDARY, Wire); + checkIaqSensorStatus(); + + iaqSensor.setConfig(bsec_config_iaq); + checkIaqSensorStatus(); + + bsec_virtual_sensor_t sensorList[7] = { + BSEC_OUTPUT_RAW_TEMPERATURE, + BSEC_OUTPUT_RAW_PRESSURE, + BSEC_OUTPUT_RAW_HUMIDITY, + BSEC_OUTPUT_RAW_GAS, + BSEC_OUTPUT_IAQ_ESTIMATE, + BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_TEMPERATURE, + BSEC_OUTPUT_SENSOR_HEAT_COMPENSATED_HUMIDITY, + }; + + knx.setSaveCallback(saveBme680State); + knx.setRestoreCallback(loadBme680State); + + if (knx.configured()) + { + cyclSend = knx.paramInt(0); + Serial.printf("Zykl. send: %d\n", cyclSend); + } + + // start the framework. Will get wifi first. + knx.start(); + + iaqSensor.updateSubscription(sensorList, 7, BSEC_SAMPLE_RATE_LP); + checkIaqSensorStatus(); + String output = "Timestamp [ms], raw temperature [°C], pressure [hPa], raw relative humidity [%], gas [Ohm], IAQ, IAQ accuracy, temperature [°C], relative humidity [%]"; + Serial.println(output); +} + +// Function that is looped forever +void loop(void) +{ + // don't delay here to much. Otherwise you might lose packages or mess up the timing with ETS + knx.loop(); + + // only run the application code if the device was configured with ETS + if(!knx.configured()) + return; + + if (iaqSensor.run()) + { + String output = String(millis()); + output += ", " + String(iaqSensor.rawTemperature); + output += ", " + String(iaqSensor.pressure); + output += ", " + String(iaqSensor.rawHumidity); + output += ", " + String(iaqSensor.gasResistance); + output += ", " + String(iaqSensor.iaqEstimate); + output += ", " + String(iaqSensor.iaqAccuracy); + output += ", " + String(iaqSensor.temperature); + output += ", " + String(iaqSensor.humidity); + Serial.println(output); + updateState(); + + if (sendCounter++ == cyclSend || trigger) + { + sendCounter = 0; + trigger = false; + + goRawTemperature.objectWrite(iaqSensor.rawTemperature); + goPressure.objectWrite(iaqSensor.pressure); + goRawHumidity.objectWrite(iaqSensor.rawHumidity); + goGasResistance.objectWrite(iaqSensor.gasResistance); + goIaqEstimate.objectWrite(iaqSensor.iaqEstimate); + goIaqAccurace.objectWrite(iaqSensor.iaqAccuracy); + goTemperature.objectWrite(iaqSensor.temperature); + goHumidity.objectWrite(iaqSensor.humidity); + } + } + else { + checkIaqSensorStatus(); + } +} + +// Helper function definitions +void checkIaqSensorStatus(void) +{ + if (iaqSensor.status != BSEC_OK) { + if (iaqSensor.status < BSEC_OK) { + String output = "BSEC error code : " + String(iaqSensor.status); + Serial.println(output); + for (;;) + errLeds(); /* Halt in case of failure */ + } + else { + String output = "BSEC warning code : " + String(iaqSensor.status); + Serial.println(output); + } + } + + if (iaqSensor.bme680Status != BME680_OK) { + if (iaqSensor.bme680Status < BME680_OK) { + String output = "BME680 error code : " + String(iaqSensor.bme680Status); + Serial.println(output); + for (;;) + errLeds(); /* Halt in case of failure */ + } + else { + String output = "BME680 warning code : " + String(iaqSensor.bme680Status); + Serial.println(output); + } + } +} + +void errLeds(void) +{ + pinMode(LED_BUILTIN, OUTPUT); + digitalWrite(LED_BUILTIN, HIGH); + delay(100); + digitalWrite(LED_BUILTIN, LOW); + delay(100); +} + +uint8_t* loadBme680State(uint8_t* buffer) +{ + // Existing state in EEPROM + Serial.println("Reading state from EEPROM"); + + for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE; i++) { + Serial.println(buffer[i], HEX); + } + + iaqSensor.setState(buffer); + checkIaqSensorStatus(); + return buffer + BSEC_MAX_STATE_BLOB_SIZE; +} + +uint8_t* saveBme680State(uint8_t* buffer) +{ + iaqSensor.getState(buffer); + checkIaqSensorStatus(); + + Serial.println("Writing state to EEPROM"); + + for (uint8_t i = 0; i < BSEC_MAX_STATE_BLOB_SIZE; i++) { + Serial.println(buffer[i], HEX); + } + return buffer + BSEC_MAX_STATE_BLOB_SIZE; +} + +void updateState(void) +{ + bool update = false; + if (stateUpdateCounter == 0) { + /* First state update when IAQ accuracy is >= 1 */ + if (iaqSensor.iaqAccuracy >= 3) { + update = true; + stateUpdateCounter++; + } + } + else { + /* Update every STATE_SAVE_PERIOD minutes */ + if ((stateUpdateCounter * STATE_SAVE_PERIOD) < millis()) { + update = true; + stateUpdateCounter++; + } + } + + if (update) { + knx.writeMemory(); + } +} + +// callback from trigger-GO +void triggerCallback(GroupObject& go) +{ + Serial.println("trigger"); + Serial.println(go.objectReadBool()); + if (!go.objectReadBool()) + return; + + trigger = true; + /* We call bsec_update_subscription() in order to instruct BSEC to perform an extra measurement at the next + possible time slot + */ + Serial.println("Triggering ULP plus."); + bsec_virtual_sensor_t sensorList[1] = { + BSEC_OUTPUT_IAQ_ESTIMATE, + }; + + iaqSensor.updateSubscription(sensorList, 1, BSEC_SAMPLE_RATE_ULP_MEASUREMENT_ON_DEMAND); + checkIaqSensorStatus(); +} \ No newline at end of file diff --git a/visualstudio/knx-bme680.vgdbproj b/visualstudio/knx-bme680.vgdbproj new file mode 100644 index 0000000..86b5086 --- /dev/null +++ b/visualstudio/knx-bme680.vgdbproj @@ -0,0 +1,283 @@ + + + Release + + + + Unknown + + + 58afeecd-06e2-4bb7-a13f-e1d5dbaed13f + true + true + ..\examples\knx-esp-bme680 + basic_config_state_ulp_plus.ino + + + + knx-bme680.ino + + + + + + + + .. + ..\.. + + Output\$(PlatformName.defuse)\$(ConfigurationName.defuse) + false + + + + + + + + + + + + + Default + + + + COM5 + + 115200 + 8 + None + One + None + + + 0 + false + false + true + ASCII + + + 255 + 0 + 0 + 0 + + + 255 + 169 + 169 + 169 + + + 255 + 211 + 211 + 211 + + + 255 + 144 + 238 + 144 + + + 255 + 169 + 169 + 169 + + + + 16 + true + true + true + true + 0 + + LF + true + true + true + + + + true + + + + + True + + true + true + + Enabled + true + true + true + + true + + + + false + + apiModeling.google.GTest + core.builtin.BuiltinFunctions + core.builtin.NoReturnFunctions + core.CallAndMessage + core.DivideZero + core.DynamicTypePropagation + core.NonnilStringConstants + core.NonNullParamChecker + core.NullDereference + core.StackAddressEscape + core.UndefinedBinaryOperatorResult + core.uninitialized.ArraySubscript + core.uninitialized.Assign + core.uninitialized.Branch + core.uninitialized.CapturedBlockVariable + core.uninitialized.UndefReturn + core.VLASize + cplusplus.NewDelete + cplusplus.NewDeleteLeaks + cplusplus.SelfAssignment + deadcode.DeadStores + nullability.NullPassedToNonnull + nullability.NullReturnedFromNonnull + security.insecureAPI.getpw + security.insecureAPI.gets + security.insecureAPI.mkstemp + security.insecureAPI.mktemp + security.insecureAPI.UncheckedReturn + security.insecureAPI.vfork + unix.API + unix.cstring.BadSizeArg + unix.cstring.NullArg + unix.Malloc + unix.MallocSizeof + unix.MismatchedDeallocator + unix.StdCLibraryFunctions + unix.Vfork + + + -analyzer-store=region + -analyzer-opt-analyze-nested-blocks + -analyzer-eagerly-assume + + + + + + NodeMCU 1 0 (ESP-12E Module) + + + + + false + false + false + false + false + false + false + false + false + + false + false + false + false + false + false + true + false + None + false + false + main + true + false + false + false + 0 + + + gdbstub + + 74880 + 115200 + 50 + !DTR;RTS;SLEEP;DTR;!RTS;SLEEP;!DTR;SLEEP + Enabled + false + + size4M + freq40M + QIO + + + + true + Disabled + 0 + false + false + true + false + false + + _estack + 0 + false + true + + + esp8266:esp8266:nodemcuv2 + COM5 + + + + CpuFrequency + 80 + + + VTable + flash + + + FlashSize + 4M1M + + + LwIPVariant + v2mss536 + + + Debug + Serial + + + DebugLevel + None____ + + + FlashErase + none + + + UploadSpeed + 115200 + + + + Sketch + + + + + \ No newline at end of file From 8bea44873b75785059600ee17e32c547c72b9a7c Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Sat, 27 Oct 2018 22:42:56 +0200 Subject: [PATCH 23/24] update submodule --- src/knx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/knx b/src/knx index fd2334a..0762e75 160000 --- a/src/knx +++ b/src/knx @@ -1 +1 @@ -Subproject commit fd2334a4af59e9cea5325d399afc639517a5d41a +Subproject commit 0762e753f73a44ca77a266753d8981203e579a32 From cdbf3e70cca72fbcd377eb458ee73e199e8f0af4 Mon Sep 17 00:00:00 2001 From: Thomas Kunze Date: Wed, 7 Nov 2018 00:32:36 +0100 Subject: [PATCH 24/24] include linux and samd versions here --- .../bme680.knxprod | Bin .../{knx-esp-bme680 => knx-bme680}/bme680.xml | 0 .../knx-bme680.ino | 3 +- .../knx-demo-ip.knxprod} | Bin .../knx-demo-ip.xml} | 0 examples/knx-demo/knx-demo-tp.knxprod | Bin 0 -> 41318 bytes examples/knx-demo/knx-demo-tp.xml | 133 +++++++++ .../knx-demo.ino} | 12 +- src/arch_config.h | 7 + src/button.cpp | 27 +- src/button.h | 4 + src/esp_platform.cpp | 3 + src/esp_platform.h | 4 + src/knx | 2 +- src/knx_facade.cpp | 25 +- src/knx_facade.h | 19 +- src/knx_facade_samd.cpp | 174 +++++++++++ src/led.cpp | 4 +- src/led.h | 6 +- src/linux_platform.cpp | 279 ++++++++++++++++++ src/linux_platform.h | 51 ++++ src/main.cpp | 116 ++++++++ src/nowifistate.cpp | 10 +- src/nowifistate.h | 6 +- src/programmingmodestate.cpp | 2 + src/programmingmodestate.h | 6 +- src/runningstate.cpp | 3 + src/runningstate.h | 5 +- src/samd_platform.cpp | 157 ++++++++++ src/samd_platform.h | 50 ++++ src/state.cpp | 4 + src/state.h | 7 +- src/wpsstate.cpp | 5 + src/wpsstate.h | 3 + visualstudio/ClassDiagram.cd | 95 ++++++ visualstudio/knx-bme680.vgdbproj | 59 +++- .../{knx-esp.vgdbproj => knx-demo.vgdbproj} | 67 ++++- visualstudio/knx-linux-Debug.vgdbsettings | 148 ++++++++++ visualstudio/knx-linux-Release.vgdbsettings | 148 ++++++++++ visualstudio/knx-linux.vcxproj | 131 ++++++++ visualstudio/knx-linux.vcxproj.filters | 217 ++++++++++++++ visualstudio/knx.sln | 57 ++++ 42 files changed, 2014 insertions(+), 35 deletions(-) rename examples/{knx-esp-bme680 => knx-bme680}/bme680.knxprod (100%) rename examples/{knx-esp-bme680 => knx-bme680}/bme680.xml (100%) rename examples/{knx-esp-bme680 => knx-bme680}/knx-bme680.ino (99%) rename examples/{knx-esp-demo/knx-esp-demo.knxprod => knx-demo/knx-demo-ip.knxprod} (100%) rename examples/{knx-esp-demo/knx-esp-demo.xml => knx-demo/knx-demo-ip.xml} (100%) create mode 100644 examples/knx-demo/knx-demo-tp.knxprod create mode 100644 examples/knx-demo/knx-demo-tp.xml rename examples/{knx-esp-demo/knx-esp-demo.ino => knx-demo/knx-demo.ino} (83%) create mode 100644 src/arch_config.h create mode 100644 src/knx_facade_samd.cpp create mode 100644 src/linux_platform.cpp create mode 100644 src/linux_platform.h create mode 100644 src/main.cpp create mode 100644 src/samd_platform.cpp create mode 100644 src/samd_platform.h create mode 100644 visualstudio/ClassDiagram.cd rename visualstudio/{knx-esp.vgdbproj => knx-demo.vgdbproj} (74%) create mode 100644 visualstudio/knx-linux-Debug.vgdbsettings create mode 100644 visualstudio/knx-linux-Release.vgdbsettings create mode 100644 visualstudio/knx-linux.vcxproj create mode 100644 visualstudio/knx-linux.vcxproj.filters create mode 100644 visualstudio/knx.sln diff --git a/examples/knx-esp-bme680/bme680.knxprod b/examples/knx-bme680/bme680.knxprod similarity index 100% rename from examples/knx-esp-bme680/bme680.knxprod rename to examples/knx-bme680/bme680.knxprod diff --git a/examples/knx-esp-bme680/bme680.xml b/examples/knx-bme680/bme680.xml similarity index 100% rename from examples/knx-esp-bme680/bme680.xml rename to examples/knx-bme680/bme680.xml diff --git a/examples/knx-esp-bme680/knx-bme680.ino b/examples/knx-bme680/knx-bme680.ino similarity index 99% rename from examples/knx-esp-bme680/knx-bme680.ino rename to examples/knx-bme680/knx-bme680.ino index 1eee699..96de618 100644 --- a/examples/knx-esp-bme680/knx-bme680.ino +++ b/examples/knx-bme680/knx-bme680.ino @@ -90,7 +90,8 @@ void setup(void) if (knx.configured()) { cyclSend = knx.paramInt(0); - Serial.printf("Zykl. send: %d\n", cyclSend); + Serial.print("Zykl. send:"); + Serial.println(cyclSend); } // start the framework. Will get wifi first. diff --git a/examples/knx-esp-demo/knx-esp-demo.knxprod b/examples/knx-demo/knx-demo-ip.knxprod similarity index 100% rename from examples/knx-esp-demo/knx-esp-demo.knxprod rename to examples/knx-demo/knx-demo-ip.knxprod diff --git a/examples/knx-esp-demo/knx-esp-demo.xml b/examples/knx-demo/knx-demo-ip.xml similarity index 100% rename from examples/knx-esp-demo/knx-esp-demo.xml rename to examples/knx-demo/knx-demo-ip.xml diff --git a/examples/knx-demo/knx-demo-tp.knxprod b/examples/knx-demo/knx-demo-tp.knxprod new file mode 100644 index 0000000000000000000000000000000000000000..ccb136353a00735032916bfe4dcfd1f3a3969ad1 GIT binary patch literal 41318 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 z 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 literal 0 HcmV?d00001 diff --git a/examples/knx-demo/knx-demo-tp.xml b/examples/knx-demo/knx-demo-tp.xml new file mode 100644 index 0000000..b7ae008 --- /dev/null +++ b/examples/knx-demo/knx-demo-tp.xml @@ -0,0 +1,133 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/knx-esp-demo/knx-esp-demo.ino b/examples/knx-demo/knx-demo.ino similarity index 83% rename from examples/knx-esp-demo/knx-esp-demo.ino rename to examples/knx-demo/knx-demo.ino index b7ae38c..80e5473 100644 --- a/examples/knx-esp-demo/knx-esp-demo.ino +++ b/examples/knx-demo/knx-demo.ino @@ -1,4 +1,3 @@ -#include #include // declare array of all groupobjects with their sizes in byte @@ -62,7 +61,6 @@ void resetCallback(GroupObject& go) void setup() { Serial.begin(115200); - Serial.setDebugOutput(true); randomSeed(millis()); @@ -77,11 +75,11 @@ void setup() // print values of parameters if device is already configured if (knx.configured()) { - Serial.printf("Timeout: %d\n", knx.paramByte(0)); - Serial.printf("Zykl. senden: %d\n", knx.paramByte(1)); - Serial.printf("Min/Max senden: %d\n", knx.paramByte(2)); - Serial.printf("Aenderung senden: %d\n", knx.paramByte(3)); - Serial.printf("Abgleich %d\n", knx.paramByte(4)); + SerialDBG.print("Timeout: "); SerialDBG.println(knx.paramByte(0)); + SerialDBG.print("Zykl. senden: "); SerialDBG.println(knx.paramByte(1)); + SerialDBG.print("Min/Max senden: "); SerialDBG.println(knx.paramByte(2)); + SerialDBG.print("Aenderung senden: "); SerialDBG.println(knx.paramByte(3)); + SerialDBG.print("Abgleich: "); SerialDBG.println(knx.paramByte(4)); } // start the framework. Will get wifi first. diff --git a/src/arch_config.h b/src/arch_config.h new file mode 100644 index 0000000..044a407 --- /dev/null +++ b/src/arch_config.h @@ -0,0 +1,7 @@ +#pragma once + +#ifdef ARDUINO_ARCH_ESP8266 +#ifndef USE_STATES +#define USE_STATES +#endif +#endif \ No newline at end of file diff --git a/src/button.cpp b/src/button.cpp index e5a719b..5b014a1 100644 --- a/src/button.cpp +++ b/src/button.cpp @@ -2,9 +2,19 @@ #include "state.h" #include "knx_facade.h" +#ifdef USE_STATES unsigned long buttonTimestamp = 0; + +void buttonDown() +{ + buttonTimestamp = millis(); + attachInterrupt(knx.buttonPin(), buttonUp, RISING); +} +#endif + void buttonUp() { +#ifdef USE_STATES if (millis() - buttonTimestamp > 1000) { Serial.println("long button press"); @@ -16,10 +26,15 @@ void buttonUp() currentState->shortButtonPress(); } attachInterrupt(knx.buttonPin(), buttonDown, FALLING); -} - -void buttonDown() -{ - buttonTimestamp = millis(); - attachInterrupt(knx.buttonPin(), buttonUp, RISING); +#else if (knx.progMode()) + { + digitalWrite(knx.ledPin(), LOW); + knx.progMode(false); + } + else + { + digitalWrite(knx.ledPin(), HIGH); + knx.progMode(true); + } +#endif } \ No newline at end of file diff --git a/src/button.h b/src/button.h index 1c32762..4f921c1 100644 --- a/src/button.h +++ b/src/button.h @@ -1,4 +1,8 @@ #pragma once +#include "arch_config.h" + +#ifdef USE_STATES void buttonDown(); +#endif void buttonUp(); \ No newline at end of file diff --git a/src/esp_platform.cpp b/src/esp_platform.cpp index 017ff11..f68b4c0 100644 --- a/src/esp_platform.cpp +++ b/src/esp_platform.cpp @@ -1,4 +1,6 @@ #include "esp_platform.h" + +#ifdef ARDUINO_ARCH_ESP8266 #include #include #include @@ -169,3 +171,4 @@ size_t EspPlatform::readBytesUart(uint8_t *buffer, size_t length) printHex("p>", buffer, length); return length; } +#endif \ No newline at end of file diff --git a/src/esp_platform.h b/src/esp_platform.h index 4d5bd08..a07361d 100644 --- a/src/esp_platform.h +++ b/src/esp_platform.h @@ -1,7 +1,10 @@ +#ifdef ARDUINO_ARCH_ESP8266 #include "knx/platform.h" #include #include +#define SerialDBG Serial + class EspPlatform : public Platform { public: @@ -43,3 +46,4 @@ private: WiFiUDP _udp; }; +#endif \ No newline at end of file diff --git a/src/knx b/src/knx index 0762e75..aaf6c27 160000 --- a/src/knx +++ b/src/knx @@ -1 +1 @@ -Subproject commit 0762e753f73a44ca77a266753d8981203e579a32 +Subproject commit aaf6c275843ddcb541e8fdae4c8870ede1242370 diff --git a/src/knx_facade.cpp b/src/knx_facade.cpp index 0c4956a..efe1e20 100644 --- a/src/knx_facade.cpp +++ b/src/knx_facade.cpp @@ -4,9 +4,17 @@ #include "led.h" #include "nowifistate.h" -KnxFacade knx; +#ifdef ARDUINO_ARCH_SAMD +SamdPlatform platform; +Bau07B0 bau(platform); +#else +EspPlatform platform; +Bau57B0 bau(platform); +#endif +KnxFacade knx(bau); -KnxFacade::KnxFacade() : _bau(_platform) + +KnxFacade::KnxFacade(BauSystemB& bau) : _bau(bau) { manufacturerId(0xfa); _bau.addSaveRestore(this); @@ -69,8 +77,12 @@ void KnxFacade::writeMemory() void KnxFacade::loop() { +#ifdef USE_STATES if (currentState) currentState->loop(); +#else + knxLoop(); +#endif } void KnxFacade::knxLoop() @@ -112,12 +124,17 @@ void KnxFacade::start() { pinMode(_ledPin, OUTPUT); - pinMode(_buttonPin, INPUT); - attachInterrupt(_buttonPin, buttonDown, FALLING); + pinMode(_buttonPin, INPUT_PULLUP); +#ifdef USE_STATES + attachInterrupt(_buttonPin, buttonDown, FALLING); switchToSate(noWifiState); checkStates(); _ticker.attach_ms(100, doLed); +#else + attachInterrupt(knx.buttonPin(), buttonUp, RISING); + enabled(true); +#endif } uint8_t* KnxFacade::paramData(uint32_t addr) diff --git a/src/knx_facade.h b/src/knx_facade.h index 19fb3bc..6cd9fd1 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -1,16 +1,28 @@ #pragma once + +#include "arch_config.h" + +#ifdef ARDUINO_ARCH_SAMD +#include "samd_platform.h" +#include "knx/bau07B0.h" +#endif + +#ifdef ARDUINO_ARCH_ESP8266 #include #include "esp_platform.h" #include "knx/bau57B0.h" +#endif +#ifdef USE_STATES class RunningState; +#endif typedef uint8_t* (*saveRestoreCallback)(uint8_t* buffer); class KnxFacade : private SaveRestore { public: - KnxFacade(); + KnxFacade(BauSystemB& bau); bool enabled(); void enabled(bool value); bool progMode(); @@ -38,11 +50,12 @@ public: uint16_t paramWord(uint32_t addr); uint32_t paramInt(uint32_t addr); private: - EspPlatform _platform; - Bau57B0 _bau; + BauSystemB& _bau; uint32_t _ledPin = 16; uint32_t _buttonPin = 0; +#ifdef USE_STATES Ticker _ticker; +#endif saveRestoreCallback _saveCallback = 0; saveRestoreCallback _restoreCallback = 0; diff --git a/src/knx_facade_samd.cpp b/src/knx_facade_samd.cpp new file mode 100644 index 0000000..db2e2f4 --- /dev/null +++ b/src/knx_facade_samd.cpp @@ -0,0 +1,174 @@ +#include "knx_facade.h" +#if 0 +KnxFacade knx; + + +#define SerialDBG SerialUSB + +void buttonUp(); +long buttonTimestamp = 0; +void buttonDown() +{ + buttonTimestamp = millis(); + attachInterrupt(knx.buttonPin(), buttonUp, RISING); +} + +void buttonUp() +{ + // keep short/long for now + if (millis() - buttonTimestamp > 1000) + { + SerialDBG.println("long button press"); + } + else + { + SerialDBG.println("short button press"); + } + + if (knx.progMode()) + { + digitalWrite(knx.ledPin(), LOW); + knx.progMode(false); + } + else + { + digitalWrite(knx.ledPin(), HIGH); + knx.progMode(true); + } + + attachInterrupt(knx.buttonPin(), buttonDown, FALLING); +} + +KnxFacade::KnxFacade() : _bau(_platform) +{ + manufacturerId(0xfa); +} + +bool KnxFacade::enabled() +{ + return _bau.enabled(); +} + +void KnxFacade::enabled(bool value) +{ + _bau.enabled(true); +} + +bool KnxFacade::progMode() +{ + return _bau.deviceObject().progMode(); +} + +void KnxFacade::progMode(bool value) +{ + _bau.deviceObject().progMode(value); +} + +bool KnxFacade::configured() +{ + return _bau.configured(); +} + +uint32_t KnxFacade::ledPin() +{ + return _ledPin; +} + +void KnxFacade::ledPin(uint32_t value) +{ + _ledPin = value; +} + +uint32_t KnxFacade::buttonPin() +{ + return _buttonPin; +} + +void KnxFacade::buttonPin(uint32_t value) +{ + _buttonPin = value; +} + +void KnxFacade::readMemory() +{ + _bau.readMemory(); +} + +void KnxFacade::loop() +{ + _bau.loop(); +} + +void KnxFacade::registerGroupObjects(GroupObject* groupObjects, uint16_t count) +{ + _bau.groupObjectTable().groupObjects(groupObjects, count); +} + +void KnxFacade::manufacturerId(uint16_t value) +{ + _bau.deviceObject().manufacturerId(value); +} + +void KnxFacade::bauNumber(uint32_t value) +{ + _bau.deviceObject().bauNumber(value); +} + +void KnxFacade::orderNumber(const char* value) +{ + _bau.deviceObject().orderNumber(value); +} + +void KnxFacade::hardwareType(uint8_t* value) +{ + _bau.deviceObject().hardwareType(value); +} + +void KnxFacade::version(uint16_t value) +{ + _bau.deviceObject().version(value); +} + +void KnxFacade::start() +{ + pinMode(_ledPin, OUTPUT); + + pinMode(_buttonPin, INPUT_PULLUP); + attachInterrupt(_buttonPin, buttonDown, FALLING); + enabled(true); +} + +uint8_t* KnxFacade::paramData(uint32_t addr) +{ + if (!_bau.configured()) + return nullptr; + + return _bau.parameters().data(addr); +} + +uint8_t KnxFacade::paramByte(uint32_t addr) +{ + if (!_bau.configured()) + return 0; + + return _bau.parameters().getByte(addr); +} + +uint16_t KnxFacade::paramWord(uint32_t addr) +{ + if (!_bau.configured()) + return 0; + + return _bau.parameters().getWord(addr); +} + +uint32_t KnxFacade::paramInt(uint32_t addr) +{ + if (!_bau.configured()) + return 0; + + return _bau.parameters().getInt(addr); +} + + +#endif \ No newline at end of file diff --git a/src/led.cpp b/src/led.cpp index e1a66a6..41030f2 100644 --- a/src/led.cpp +++ b/src/led.cpp @@ -2,6 +2,7 @@ #include "knx_facade.h" #include "state.h" +#ifdef USE_STATES void doLed() { if (!currentState) @@ -25,4 +26,5 @@ void doLed() digitalWrite(knx.ledPin(), HIGH); else digitalWrite(knx.ledPin(), LOW); -} \ No newline at end of file +} +#endif \ No newline at end of file diff --git a/src/led.h b/src/led.h index 985759a..fadcac6 100644 --- a/src/led.h +++ b/src/led.h @@ -1,3 +1,7 @@ #pragma once -void doLed(); \ No newline at end of file +#include "arch_config.h" + +#ifdef USE_STATES +void doLed(); +#endif \ No newline at end of file diff --git a/src/linux_platform.cpp b/src/linux_platform.cpp new file mode 100644 index 0000000..229f4c0 --- /dev/null +++ b/src/linux_platform.cpp @@ -0,0 +1,279 @@ +#include "linux_platform.h" +#ifdef __linux__ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "knx/device_object.h" +#include "knx/address_table_object.h" +#include "knx/association_table_object.h" +#include "knx/group_object_table_object.h" +#include "knx/application_program_object.h" +#include "knx/ip_parameter_object.h" + +LinuxPlatform::LinuxPlatform() +{ + doMemoryMapping(); +} + +uint32_t LinuxPlatform::currentIpAddress() +{ + return 0; +} + +uint32_t LinuxPlatform::currentSubnetMask() +{ + return 0; +} + +uint32_t LinuxPlatform::currentDefaultGateway() +{ + return 0; +} + +uint32_t LinuxPlatform::millis() +{ + struct timespec spec; + + clock_gettime(CLOCK_MONOTONIC, &spec); + return spec.tv_sec * 1000 + round(spec.tv_nsec / 1.0e6); +} + +void LinuxPlatform::mdelay(uint32_t millis) +{ + struct timespec ts; + ts.tv_sec = millis / 1000; + ts.tv_nsec = (millis % 1000) * 1000000; + nanosleep(&ts, NULL); +} + +void LinuxPlatform::macAddress(uint8_t* data) +{ + // hardcode some address + data[0] = 0x08; + data[1] = 0x00; + data[2] = 0x27; + data[3] = 0x6c; + data[4] = 0xa8; + data[5] = 0x2a; +} + +void LinuxPlatform::restart() +{ + // do nothing +} + +void LinuxPlatform::fatalError() +{ + printf("A fatal error occured. Stopping.\n"); + while (true) + sleep(1); +} + +void LinuxPlatform::setupMultiCast(uint32_t addr, uint16_t port) +{ + _multicastAddr = addr; + _port = port; + + struct ip_mreq command; + uint32_t loop = 1; + + struct sockaddr_in sin; + memset(&sin, 0, sizeof(sin)); + sin.sin_family = AF_INET; + sin.sin_addr.s_addr = htonl(INADDR_ANY); + sin.sin_port = htons(port); + + _socketFd = socket(AF_INET, SOCK_DGRAM, 0); + if (_socketFd == -1) { + perror("socket()"); + fatalError(); + } + + /* Mehr Prozessen erlauben, denselben Port zu nutzen */ + loop = 1; + if (setsockopt(_socketFd, SOL_SOCKET, SO_REUSEADDR, &loop, sizeof(loop)) < 0) + { + perror("setsockopt:SO_REUSEADDR"); + fatalError(); + } + + if (bind(_socketFd, (struct sockaddr *)&sin, sizeof(sin)) < 0) + { + perror("bind"); + fatalError(); + } + + /* Broadcast auf dieser Maschine zulassen */ + loop = 1; + if (setsockopt(_socketFd, IPPROTO_IP, IP_MULTICAST_LOOP, &loop, sizeof(loop)) < 0) + { + perror("setsockopt:IP_MULTICAST_LOOP"); + fatalError(); + } + + /* Join the broadcast group: */ + command.imr_multiaddr.s_addr = htonl(addr); + command.imr_interface.s_addr = htonl(INADDR_ANY); + + if (setsockopt(_socketFd, IPPROTO_IP, IP_ADD_MEMBERSHIP, &command, sizeof(command)) < 0) + { + perror("setsockopt:IP_ADD_MEMBERSHIP"); + fatalError(); + } + + uint32_t flags = fcntl(_socketFd, F_GETFL); + flags |= O_NONBLOCK; + fcntl(_socketFd, F_SETFL, flags); +} + +void LinuxPlatform::closeMultiCast() +{ + struct ip_mreq command; + command.imr_multiaddr.s_addr = htonl(_multicastAddr); + command.imr_interface.s_addr = htonl(INADDR_ANY); + + if (setsockopt(_socketFd, + IPPROTO_IP, + IP_DROP_MEMBERSHIP, + &command, sizeof(command)) < 0) { + perror("setsockopt:IP_DROP_MEMBERSHIP"); + } + close(_socketFd); +} + +bool LinuxPlatform::sendBytes(uint8_t* buffer, uint16_t len) +{ + struct sockaddr_in address = { 0 }; + address.sin_family = AF_INET; + address.sin_addr.s_addr = htonl(_multicastAddr); + address.sin_port = htons(_port); + + ssize_t retVal = 0; + do + { + retVal = sendto(_socketFd, buffer, len, 0, (struct sockaddr *) &address, sizeof(address)); + if (retVal == -1) + { + if (errno != EAGAIN && errno != EWOULDBLOCK) + return false; + } + } while (retVal == -1); + return true; +} + +int LinuxPlatform::readBytes(uint8_t * buffer, uint16_t maxLen) +{ + uint32_t sin_len; + struct sockaddr_in sin; + + sin_len = sizeof(sin); + ssize_t len = recvfrom(_socketFd, buffer, maxLen, 0, (struct sockaddr *) &sin, &sin_len); + return len; +} + +uint8_t * LinuxPlatform::getEepromBuffer(uint16_t size) +{ + return _mappedFile + 2; +} + +void LinuxPlatform::commitToEeprom() +{ + fsync(_fd); +} + +#define FLASHSIZE 0x10000 +void LinuxPlatform::doMemoryMapping() +{ + _fd = open("flash.bin", O_RDWR | O_CREAT, S_IRWXU | S_IRGRP | S_IROTH); + if (_fd < 0) + { + perror("Error in file opening"); + //exit(-1); + } + + struct stat st; + uint32_t ret = fstat(_fd, &st); + if (ret < 0) + { + perror("Error in fstat"); + //exit(-1); + } + + size_t len_file = st.st_size; + if (len_file < FLASHSIZE) + { + if (ftruncate(_fd, FLASHSIZE) != 0) + { + perror("Error extending file"); + //exit(-1); + } + len_file = FLASHSIZE; + } + unsigned char* addr = (unsigned char*)mmap(NULL, len_file, PROT_READ | PROT_WRITE, MAP_SHARED, _fd, 0); + if (addr[0] != 0xAF || addr[1] != 0xFE) + { + memset(addr, 0, FLASHSIZE); + addr[0] = 0xAF; + addr[1] = 0xFE; + } + + if (addr == MAP_FAILED) + { + perror("Error in mmap"); + //exit(-1); + } + _mappedFile = addr; +} + +size_t LinuxPlatform::readBytesUart(uint8_t *buffer, size_t length) +{ + return 0; +} + + +int LinuxPlatform::readUart() +{ + return -1; +} + + +size_t LinuxPlatform::writeUart(const uint8_t *buffer, size_t size) +{ + return 0; +} + + +size_t LinuxPlatform::writeUart(const uint8_t data) +{ + return 0; +} + + +int LinuxPlatform::uartAvailable() +{ + return 0; +} + + +void LinuxPlatform::closeUart() +{ +} + + +void LinuxPlatform::setupUart() +{ +} +#endif \ No newline at end of file diff --git a/src/linux_platform.h b/src/linux_platform.h new file mode 100644 index 0000000..710b9fa --- /dev/null +++ b/src/linux_platform.h @@ -0,0 +1,51 @@ +#pragma once + +#ifdef __linux__ + +#include "knx/platform.h" + +class LinuxPlatform: public Platform +{ +public: + LinuxPlatform(); + + // ip stuff + uint32_t currentIpAddress(); + uint32_t currentSubnetMask(); + uint32_t currentDefaultGateway(); + void macAddress(uint8_t* addr); + + // basic stuff + uint32_t millis(); + void mdelay(uint32_t millis); + void restart(); + void fatalError(); + + //multicast + void setupMultiCast(uint32_t addr, uint16_t port); + void closeMultiCast(); + bool sendBytes(uint8_t* buffer, uint16_t len); + int readBytes(uint8_t* buffer, uint16_t maxLen); + + //uart + void setupUart(); + void closeUart(); + int uartAvailable(); + size_t writeUart(const uint8_t data); + size_t writeUart(const uint8_t *buffer, size_t size); + int readUart(); + size_t readBytesUart(uint8_t *buffer, size_t length); + + //memory + uint8_t* getEepromBuffer(uint16_t size); + void commitToEeprom(); +private: + uint32_t _multicastAddr; + uint16_t _port; + int _socketFd = -1; + void doMemoryMapping(); + uint8_t* _mappedFile; + int _fd; +}; + +#endif \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..d0094f0 --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,116 @@ +#ifdef __linux__ + +#include "linux_platform.h" +#include "knx/bau57B0.h" +#include "knx/group_object_table_object.h" +#include +#include +#include + +LinuxPlatform platfrom; +Bau57B0 bau(platfrom); + +float currentValue = 0; +float maxValue = 0; +float minValue = RAND_MAX; +long lastsend = 0; + +GroupObject groupObjects[] +{ + GroupObject(2), + GroupObject(2), + GroupObject(2), + GroupObject(1) +}; +#define CURR groupObjects[0] +#define MAX groupObjects[1] +#define MIN groupObjects[2] +#define RESET groupObjects[3] + +void measureTemp() +{ + long now = platfrom.millis(); + if ((now - lastsend) < 2000) + return; + + lastsend = now; + int r = rand(); + currentValue = (r * 1.0) / (RAND_MAX * 1.0); + currentValue *= 100 * 100; + + CURR.objectWrite(currentValue); + + if (currentValue > maxValue) + { + maxValue = currentValue; + MAX.objectWrite(maxValue); + } + + if (currentValue < minValue) + { + minValue = currentValue; + MIN.objectWrite(minValue); + } +} + +void resetCallback(GroupObject& go) +{ + if (go.objectReadBool()) + { + maxValue = 0; + minValue = 10000; + } +} + +void appLoop() +{ + if (!bau.configured()) + return; + + measureTemp(); +} + +void setup() +{ + srand((unsigned int)time(NULL)); + bau.readMemory(); + + uint8_t hwType[] = { 0x0, 0x0, 0x8, 0x0, 0x0, 0x2 }; + GroupObjectTableObject& got(bau.groupObjectTable()); + got.groupObjects(groupObjects, 4); + + DeviceObject& devObj(bau.deviceObject()); + devObj.manufacturerId(0xfa); + devObj.bauNumber(0xdeadbeef); + devObj.orderNumber("Coolstuff"); + devObj.hardwareType(hwType); + devObj.version(0x0020); + + RESET.updateHandler = resetCallback; + + if (bau.deviceObject().induvidualAddress() == 0) + bau.deviceObject().progMode(true); + + if (bau.parameters().loadState() == LS_LOADED) + { + printf("Timeout: %d\n", bau.parameters().getWord(0)); + printf("Zykl. senden: %d\n", bau.parameters().getByte(2)); + printf("Min/Max senden: %d\n", bau.parameters().getByte(3)); + printf("Aenderung senden: %d\n", bau.parameters().getByte(4)); + printf("Abgleich %d\n", bau.parameters().getByte(5)); + } + bau.enabled(true); +} + +int main(int argc, char **argv) +{ + setup(); + + while (1) + { + bau.loop(); + appLoop(); + platfrom.mdelay(100); + } +} +#endif \ No newline at end of file diff --git a/src/nowifistate.cpp b/src/nowifistate.cpp index ddb6073..22feeb1 100644 --- a/src/nowifistate.cpp +++ b/src/nowifistate.cpp @@ -1,9 +1,11 @@ -#include - #include "nowifistate.h" #include "wpsstate.h" #include "runningstate.h" +#ifdef USE_STATES + +#include + NoWifiState noWifiState = NoWifiState(); void NoWifiState::shortButtonPress() @@ -32,4 +34,6 @@ void NoWifiState::enterState() Serial.printf("\nConnected successful to SSID '%s'\n", WiFi.SSID().c_str()); switchToSate(runningState); } -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/src/nowifistate.h b/src/nowifistate.h index f15d46e..f64892e 100644 --- a/src/nowifistate.h +++ b/src/nowifistate.h @@ -2,6 +2,8 @@ #include "state.h" +#ifdef USE_STATES + class NoWifiState : public State { public: @@ -13,4 +15,6 @@ public: virtual const char* name() { return "NoWifi"; } }; -extern NoWifiState noWifiState; \ No newline at end of file +extern NoWifiState noWifiState; + +#endif \ No newline at end of file diff --git a/src/programmingmodestate.cpp b/src/programmingmodestate.cpp index be52139..32ee313 100644 --- a/src/programmingmodestate.cpp +++ b/src/programmingmodestate.cpp @@ -2,6 +2,7 @@ #include "runningstate.h" #include "knx_facade.h" +#ifdef USE_STATES ProgramModeState programModeState = ProgramModeState(); void ProgramModeState::enterState() @@ -24,3 +25,4 @@ void ProgramModeState::loop() State::loop(); knx.knxLoop(); } +#endif \ No newline at end of file diff --git a/src/programmingmodestate.h b/src/programmingmodestate.h index 489fc8b..0167905 100644 --- a/src/programmingmodestate.h +++ b/src/programmingmodestate.h @@ -2,6 +2,8 @@ #include "state.h" +#ifdef USE_STATES + class ProgramModeState : public State { public: @@ -14,4 +16,6 @@ public: virtual const char* name() { return "ProgramMode"; } }; -extern ProgramModeState programModeState; \ No newline at end of file +extern ProgramModeState programModeState; + +#endif \ No newline at end of file diff --git a/src/runningstate.cpp b/src/runningstate.cpp index a13cc80..c97ef0a 100644 --- a/src/runningstate.cpp +++ b/src/runningstate.cpp @@ -3,6 +3,8 @@ #include "wpsstate.h" #include "knx_facade.h" +#ifdef USE_STATES + RunningState runningState = RunningState(); void RunningState::shortButtonPress() @@ -38,3 +40,4 @@ void RunningState::loop() State::loop(); knx.knxLoop(); } +#endif \ No newline at end of file diff --git a/src/runningstate.h b/src/runningstate.h index 374fb97..88d7b3e 100644 --- a/src/runningstate.h +++ b/src/runningstate.h @@ -1,7 +1,7 @@ #pragma once #include "state.h" - +#ifdef USE_STATES class RunningState : public State { public: @@ -17,4 +17,5 @@ private: bool _initialized = false; }; -extern RunningState runningState; \ No newline at end of file +extern RunningState runningState; +#endif \ No newline at end of file diff --git a/src/samd_platform.cpp b/src/samd_platform.cpp new file mode 100644 index 0000000..3e72a23 --- /dev/null +++ b/src/samd_platform.cpp @@ -0,0 +1,157 @@ +#include "samd_platform.h" + +#ifdef ARDUINO_ARCH_SAMD +#include + +#include +#include + +SamdPlatform::SamdPlatform() +{ +} + +uint32_t SamdPlatform::currentIpAddress() +{ + // not needed + return 0; +} + +uint32_t SamdPlatform::currentSubnetMask() +{ + // not needed + return 0; +} + +uint32_t SamdPlatform::currentDefaultGateway() +{ + // not needed + return 0; +} + +void SamdPlatform::macAddress(uint8_t * addr) +{ + // not needed +} + +uint32_t SamdPlatform::millis() +{ + return::millis(); +} + +void SamdPlatform::mdelay(uint32_t millis) +{ + delay(millis); +} + +void SamdPlatform::restart() +{ + SerialUSB.println("restart"); + NVIC_SystemReset(); +} + +void SamdPlatform::fatalError() +{ + const int period = 200; + while (true) + { + if ((millis() % period) > (period / 2)) + digitalWrite(LED_BUILTIN, HIGH); + else + digitalWrite(LED_BUILTIN, LOW); + } +} + +void SamdPlatform::setupMultiCast(uint32_t addr, uint16_t port) +{ + //not needed +} + +void SamdPlatform::closeMultiCast() +{ + //not needed +} + +bool SamdPlatform::sendBytes(uint8_t * buffer, uint16_t len) +{ + //not needed +} + +int SamdPlatform::readBytes(uint8_t * buffer, uint16_t maxLen) +{ + //not needed + return 0; +} + +uint8_t * SamdPlatform::getEepromBuffer(uint16_t size) +{ + //EEPROM.begin(size); + if(size > EEPROM_EMULATION_SIZE) + fatalError(); + + return EEPROM.getDataPtr(); +} + +void SamdPlatform::commitToEeprom() +{ + EEPROM.commit(); +} + + +void SamdPlatform::setupUart() +{ + SerialKNX.begin(19200, SERIAL_8E1); + while (!SerialKNX) + ; +} + + +void SamdPlatform::closeUart() +{ + SerialKNX.end(); +} + + +int SamdPlatform::uartAvailable() +{ + return SerialKNX.available(); +} + + +size_t SamdPlatform::writeUart(const uint8_t data) +{ + //printHex(" 0) + // printHex("p>", (uint8_t*)&val, 1); + return val; +} + + +size_t SamdPlatform::readBytesUart(uint8_t *buffer, size_t length) +{ + size_t toRead = length; + uint8_t* pos = buffer; + while (toRead > 0) + { + size_t val = SerialKNX.readBytes(pos, toRead); + pos += val; + toRead -= val; + } + //printHex("p>", buffer, length); + return length; +} + +#endif \ No newline at end of file diff --git a/src/samd_platform.h b/src/samd_platform.h new file mode 100644 index 0000000..df7acdb --- /dev/null +++ b/src/samd_platform.h @@ -0,0 +1,50 @@ +#include "knx/platform.h" + +#include "Arduino.h" + +#ifdef ARDUINO_ARCH_SAMD + +#define SerialDBG SerialUSB +#define SerialKNX Serial1 + +class SamdPlatform : public Platform +{ +public: + SamdPlatform(); + + // ip stuff + uint32_t currentIpAddress(); + uint32_t currentSubnetMask(); + uint32_t currentDefaultGateway(); + void macAddress(uint8_t* addr); + + // basic stuff + uint32_t millis(); + void mdelay(uint32_t millis); + void restart(); + void fatalError(); + + //multicast + void setupMultiCast(uint32_t addr, uint16_t port); + void closeMultiCast(); + bool sendBytes(uint8_t* buffer, uint16_t len); + int readBytes(uint8_t* buffer, uint16_t maxLen); + + //uart + 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); + + //memory + uint8_t* getEepromBuffer(uint16_t size); + void commitToEeprom(); +private: + uint32_t _mulitcastAddr; + uint16_t _mulitcastPort; +}; + +#endif \ No newline at end of file diff --git a/src/state.cpp b/src/state.cpp index bffae1d..4199f46 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -1,6 +1,8 @@ #include "state.h" #include "Arduino.h" +#ifdef USE_STATES + State* volatile currentState = 0; State* volatile nextState = 0; @@ -51,3 +53,5 @@ void State::loop() { checkStates(); } + +#endif \ No newline at end of file diff --git a/src/state.h b/src/state.h index a8b77a9..9b155b1 100644 --- a/src/state.h +++ b/src/state.h @@ -1,4 +1,7 @@ #pragma once +#include "arch_config.h" + +#ifdef USE_STATES class State { @@ -26,4 +29,6 @@ void switchToSate(State& state); void checkStates(); extern State* volatile currentState; -extern State* volatile nextState; \ No newline at end of file +extern State* volatile nextState; + +#endif \ No newline at end of file diff --git a/src/wpsstate.cpp b/src/wpsstate.cpp index 7c395a6..615f290 100644 --- a/src/wpsstate.cpp +++ b/src/wpsstate.cpp @@ -1,3 +1,6 @@ +#include "arch_config.h" + +#ifdef USE_STATES #include #include "wpsstate.h" @@ -26,3 +29,5 @@ void WpsState::enterState() } } } + +#endif diff --git a/src/wpsstate.h b/src/wpsstate.h index 5460bb4..3f985ab 100644 --- a/src/wpsstate.h +++ b/src/wpsstate.h @@ -2,6 +2,7 @@ #include "state.h" +#ifdef USE_STATES class WpsState : public State { public: @@ -12,3 +13,5 @@ public: }; extern WpsState wpsState; + +#endif diff --git a/visualstudio/ClassDiagram.cd b/visualstudio/ClassDiagram.cd new file mode 100644 index 0000000..fa67e07 --- /dev/null +++ b/visualstudio/ClassDiagram.cd @@ -0,0 +1,95 @@ + + + + + + IBAAAAAAQACAEAIAABAAAAgAAAAAAAAAAAAAAABAAEA= + knx\address_table_object.h + + + + + + AIAAAAAAAACAAAIAgCAAAAAgABAEAAAAAAAQQABAAAA= + knx\application_program_object.h + + + + + + AAAAAAAAAACAAgIAABAAQAgAAAEAAAAAAAAAAABAAEA= + knx\association_table_object.h + + + + + + AAACACEAAAGCIAKAQCAgAACgCECAAEIgAEgAAgJAAAA= + knx\device_object.h + + + + + + AAAAAEAACACAAgIAAhAATAgACAAAAAAAAAAAAABAAAA= + knx\group_object_table_object.h + + + + + + AAAAAAAAAAAAAAAAACAAAAAgAAAAAAAAAAAAABBAAAA= + knx\interface_object.h + + + + + + AAIACIAAYACAAKIAAGAQCAAgWAIAAAAGAAAAAABgAIA= + knx\ip_parameter_object.h + + + + + + AAIACIACBACAAIIAAGAASAgwyAAAAAAAAgAAAAJgAAA= + knx\table_object.h + + + + + + sKoIAAAAQIIxC4gUUEAIAAACiQEgAjQUFCCA5yEAIBw= + knx\bau.h + + + + + + ABIAAFAgAgIwCoAAUiCCQIDAiEBgADQQACAAAAKQABw= + knx\bau57B0.h + + + + + + BACCIBAAICAAICgAAABAgAAQAAAAAAgAAADAEAAAAhA= + linux_platform.h + + + + + + AAAAAAAAAACAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + knx\save_restore.h + + + + + + AAACIBAAICAAIAAAAABAgAAAAAAAAAgAAADAEAAAAhA= + knx\platform.h + + + + \ No newline at end of file diff --git a/visualstudio/knx-bme680.vgdbproj b/visualstudio/knx-bme680.vgdbproj index 86b5086..81da2fc 100644 --- a/visualstudio/knx-bme680.vgdbproj +++ b/visualstudio/knx-bme680.vgdbproj @@ -10,7 +10,7 @@ 58afeecd-06e2-4bb7-a13f-e1d5dbaed13f true true - ..\examples\knx-esp-bme680 + ..\examples\knx-bme680 basic_config_state_ulp_plus.ino @@ -278,6 +278,63 @@ Sketch + + Arduino Genuino Zero (Native USB Port) + + + + + false + false + false + false + false + false + false + false + false + + false + false + false + false + false + false + true + false + None + false + false + main + true + false + false + false + 0 + + + true + Auto + 0 + false + false + true + false + false + + _estack + 0 + false + true + + + arduino:samd:arduino_zero_native + + + + Sketch + + \ No newline at end of file diff --git a/visualstudio/knx-esp.vgdbproj b/visualstudio/knx-demo.vgdbproj similarity index 74% rename from visualstudio/knx-esp.vgdbproj rename to visualstudio/knx-demo.vgdbproj index 4234096..0dc1c7e 100644 --- a/visualstudio/knx-esp.vgdbproj +++ b/visualstudio/knx-demo.vgdbproj @@ -10,12 +10,12 @@ 6165cd6a-91a4-49fa-977a-48f22086ca8e true true - ..\examples\knx-esp-demo + ..\examples\knx-demo Sketch.ino - knx-esp-demo.ino + knx-demo.ino @@ -82,6 +82,12 @@ 238 144 + + 255 + 169 + 169 + 169 + 16 @@ -275,6 +281,63 @@ Sketch + + Arduino Genuino Zero (Native USB Port) + + + + + false + false + false + false + false + false + false + false + false + + false + false + false + false + false + false + true + false + None + false + false + main + true + false + false + false + 0 + + + true + Auto + 0 + false + false + true + false + false + + _estack + 0 + false + true + + + arduino:samd:arduino_zero_native + + + + Sketch + + \ No newline at end of file diff --git a/visualstudio/knx-linux-Debug.vgdbsettings b/visualstudio/knx-linux-Debug.vgdbsettings new file mode 100644 index 0000000..d3e6100 --- /dev/null +++ b/visualstudio/knx-linux-Debug.vgdbsettings @@ -0,0 +1,148 @@ + + + Release + + + + RemoteUnix + + + localhost + LinuxSubsystem + Linux + + + false + + localhost + LinuxSubsystem + Linux + + $(ProjectDir) + /home/tkunze/vgdb/knx-linux + + *.cpp + *.h + *.hpp + *.c + *.cc + *.cxx + *.mak + Makefile + *.txt + *.cmake + CMakeLists.txt + *.cmake + + true + true + + true + true + + false + false + false + false + false + $(ProjectDir) + + + + com.sysprogs.toolchain.default-gcc + + 0 + + + knx-linux.vcxproj + 0 + true + + + + + + + + + + + + + Default + + + + true + + + + + Unknown + + true + false + + + + + false + + + + + + + + + false + false + false + false + false + false + false + false + false + + false + false + false + false + false + false + true + false + None + false + false + main + true + false + false + false + 0 + + + + + + LANG + en_US.UTF-8 + + + + + $(TargetPath) + 2000 + + + false + Local + false + false + Auto + true + false + + \ No newline at end of file diff --git a/visualstudio/knx-linux-Release.vgdbsettings b/visualstudio/knx-linux-Release.vgdbsettings new file mode 100644 index 0000000..d3e6100 --- /dev/null +++ b/visualstudio/knx-linux-Release.vgdbsettings @@ -0,0 +1,148 @@ + + + Release + + + + RemoteUnix + + + localhost + LinuxSubsystem + Linux + + + false + + localhost + LinuxSubsystem + Linux + + $(ProjectDir) + /home/tkunze/vgdb/knx-linux + + *.cpp + *.h + *.hpp + *.c + *.cc + *.cxx + *.mak + Makefile + *.txt + *.cmake + CMakeLists.txt + *.cmake + + true + true + + true + true + + false + false + false + false + false + $(ProjectDir) + + + + com.sysprogs.toolchain.default-gcc + + 0 + + + knx-linux.vcxproj + 0 + true + + + + + + + + + + + + + Default + + + + true + + + + + Unknown + + true + false + + + + + false + + + + + + + + + false + false + false + false + false + false + false + false + false + + false + false + false + false + false + false + true + false + None + false + false + main + true + false + false + false + 0 + + + + + + LANG + en_US.UTF-8 + + + + + $(TargetPath) + 2000 + + + false + Local + false + false + Auto + true + false + + \ No newline at end of file diff --git a/visualstudio/knx-linux.vcxproj b/visualstudio/knx-linux.vcxproj new file mode 100644 index 0000000..1e732b4 --- /dev/null +++ b/visualstudio/knx-linux.vcxproj @@ -0,0 +1,131 @@ + + + + + Debug + VisualGDB + + + Release + VisualGDB + + + + 15.0 + {819E55F9-05A8-454D-B771-4A99F775DD87} + + + + + GCC + + + + GCC + + + com.sysprogs.toolchain.default-gcc + + + + + + + + + + + + localhost-lxss + + + localhost-lxss + + + + /usr/include/x86_64-linux-gnu;%(ClCompile.AdditionalIncludeDirectories) + + + + + + + + /usr/include/x86_64-linux-gnu;%(ClCompile.AdditionalIncludeDirectories) + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/visualstudio/knx-linux.vcxproj.filters b/visualstudio/knx-linux.vcxproj.filters new file mode 100644 index 0000000..5cfbad1 --- /dev/null +++ b/visualstudio/knx-linux.vcxproj.filters @@ -0,0 +1,217 @@ + + + + + {7612a532-0bb6-4a82-917a-48cfa6410e4f} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {842f6d29-354f-47a8-bfd9-0fccf0ddf144} + h;hpp;hxx;hm;inl;inc;xsd + + + {c46b2f8f-4105-4638-af5c-09a641257065} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav + + + {c818187b-4f9f-4ee9-86c3-d26e56e49bf9} + *.vgdbsettings + + + {169b6f5b-b022-4422-ace2-819bf2f5e883} + + + {4054619f-7b60-405c-96e8-311c464cf8de} + + + + + + VisualGDB settings + + + VisualGDB settings + + + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + Header files\knx + + + + + Source files\knx + + + Source files + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + Source files\knx + + + \ No newline at end of file diff --git a/visualstudio/knx.sln b/visualstudio/knx.sln new file mode 100644 index 0000000..69aad4e --- /dev/null +++ b/visualstudio/knx.sln @@ -0,0 +1,57 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28010.2050 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "knx-linux", "knx-linux.vcxproj", "{819E55F9-05A8-454D-B771-4A99F775DD87}" +EndProject +Project("{803FD0C6-D64E-4E16-9DC3-1DAEC859A3D2}") = "knx-bme680", "knx-bme680.vgdbproj", "{58AFEECD-06E2-4BB7-A13F-E1D5DBAED13F}" +EndProject +Project("{803FD0C6-D64E-4E16-9DC3-1DAEC859A3D2}") = "knx-demo", "knx-demo.vgdbproj", "{6165CD6A-91A4-49FA-977A-48F22086CA8E}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Arduino Genuino Zero (Native USB Port) = Debug|Arduino Genuino Zero (Native USB Port) + Debug|NodeMCU 1 0 (ESP-12E Module) = Debug|NodeMCU 1 0 (ESP-12E Module) + Debug|VisualGDB = Debug|VisualGDB + Release|Arduino Genuino Zero (Native USB Port) = Release|Arduino Genuino Zero (Native USB Port) + Release|NodeMCU 1 0 (ESP-12E Module) = Release|NodeMCU 1 0 (ESP-12E Module) + Release|VisualGDB = Release|VisualGDB + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {819E55F9-05A8-454D-B771-4A99F775DD87}.Debug|Arduino Genuino Zero (Native USB Port).ActiveCfg = Debug|VisualGDB + {819E55F9-05A8-454D-B771-4A99F775DD87}.Debug|NodeMCU 1 0 (ESP-12E Module).ActiveCfg = Debug|VisualGDB + {819E55F9-05A8-454D-B771-4A99F775DD87}.Debug|VisualGDB.ActiveCfg = Debug|VisualGDB + {819E55F9-05A8-454D-B771-4A99F775DD87}.Debug|VisualGDB.Build.0 = Debug|VisualGDB + {819E55F9-05A8-454D-B771-4A99F775DD87}.Release|Arduino Genuino Zero (Native USB Port).ActiveCfg = Release|VisualGDB + {819E55F9-05A8-454D-B771-4A99F775DD87}.Release|NodeMCU 1 0 (ESP-12E Module).ActiveCfg = Release|VisualGDB + {819E55F9-05A8-454D-B771-4A99F775DD87}.Release|VisualGDB.ActiveCfg = Release|VisualGDB + {819E55F9-05A8-454D-B771-4A99F775DD87}.Release|VisualGDB.Build.0 = Release|VisualGDB + {58AFEECD-06E2-4BB7-A13F-E1D5DBAED13F}.Debug|Arduino Genuino Zero (Native USB Port).ActiveCfg = Debug|Arduino Genuino Zero (Native USB Port) + {58AFEECD-06E2-4BB7-A13F-E1D5DBAED13F}.Debug|Arduino Genuino Zero (Native USB Port).Build.0 = Debug|Arduino Genuino Zero (Native USB Port) + {58AFEECD-06E2-4BB7-A13F-E1D5DBAED13F}.Debug|NodeMCU 1 0 (ESP-12E Module).ActiveCfg = Debug|NodeMCU 1 0 (ESP-12E Module) + {58AFEECD-06E2-4BB7-A13F-E1D5DBAED13F}.Debug|NodeMCU 1 0 (ESP-12E Module).Build.0 = Debug|NodeMCU 1 0 (ESP-12E Module) + {58AFEECD-06E2-4BB7-A13F-E1D5DBAED13F}.Debug|VisualGDB.ActiveCfg = Debug|Arduino Genuino Zero (Native USB Port) + {58AFEECD-06E2-4BB7-A13F-E1D5DBAED13F}.Release|Arduino Genuino Zero (Native USB Port).ActiveCfg = Release|Arduino Genuino Zero (Native USB Port) + {58AFEECD-06E2-4BB7-A13F-E1D5DBAED13F}.Release|Arduino Genuino Zero (Native USB Port).Build.0 = Release|Arduino Genuino Zero (Native USB Port) + {58AFEECD-06E2-4BB7-A13F-E1D5DBAED13F}.Release|NodeMCU 1 0 (ESP-12E Module).ActiveCfg = Release|NodeMCU 1 0 (ESP-12E Module) + {58AFEECD-06E2-4BB7-A13F-E1D5DBAED13F}.Release|NodeMCU 1 0 (ESP-12E Module).Build.0 = Release|NodeMCU 1 0 (ESP-12E Module) + {58AFEECD-06E2-4BB7-A13F-E1D5DBAED13F}.Release|VisualGDB.ActiveCfg = Release|Arduino Genuino Zero (Native USB Port) + {6165CD6A-91A4-49FA-977A-48F22086CA8E}.Debug|Arduino Genuino Zero (Native USB Port).ActiveCfg = Debug|Arduino Genuino Zero (Native USB Port) + {6165CD6A-91A4-49FA-977A-48F22086CA8E}.Debug|Arduino Genuino Zero (Native USB Port).Build.0 = Debug|Arduino Genuino Zero (Native USB Port) + {6165CD6A-91A4-49FA-977A-48F22086CA8E}.Debug|NodeMCU 1 0 (ESP-12E Module).ActiveCfg = Debug|NodeMCU 1 0 (ESP-12E Module) + {6165CD6A-91A4-49FA-977A-48F22086CA8E}.Debug|NodeMCU 1 0 (ESP-12E Module).Build.0 = Debug|NodeMCU 1 0 (ESP-12E Module) + {6165CD6A-91A4-49FA-977A-48F22086CA8E}.Debug|VisualGDB.ActiveCfg = Debug|Arduino Genuino Zero (Native USB Port) + {6165CD6A-91A4-49FA-977A-48F22086CA8E}.Release|Arduino Genuino Zero (Native USB Port).ActiveCfg = Release|Arduino Genuino Zero (Native USB Port) + {6165CD6A-91A4-49FA-977A-48F22086CA8E}.Release|Arduino Genuino Zero (Native USB Port).Build.0 = Release|Arduino Genuino Zero (Native USB Port) + {6165CD6A-91A4-49FA-977A-48F22086CA8E}.Release|NodeMCU 1 0 (ESP-12E Module).ActiveCfg = Release|NodeMCU 1 0 (ESP-12E Module) + {6165CD6A-91A4-49FA-977A-48F22086CA8E}.Release|NodeMCU 1 0 (ESP-12E Module).Build.0 = Release|NodeMCU 1 0 (ESP-12E Module) + {6165CD6A-91A4-49FA-977A-48F22086CA8E}.Release|VisualGDB.ActiveCfg = Release|Arduino Genuino Zero (Native USB Port) + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {721CAB98-83F8-4035-AC23-77AEA55B634D} + EndGlobalSection +EndGlobal