diff --git a/src/knx/application_layer.cpp b/src/knx/application_layer.cpp index c6aedb0c..1e8ccec6 100644 --- a/src/knx/application_layer.cpp +++ b/src/knx/application_layer.cpp @@ -551,7 +551,7 @@ void ApplicationLayer::systemNetworkParameterReadResponse(Priority priority, Hop void ApplicationLayer::domainAddressSerialNumberReadResponse(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* rfDoA, const uint8_t* knxSerialNumber) { - CemiFrame frame(11); + CemiFrame frame(13); APDU& apdu = frame.apdu(); apdu.type(DomainAddressSerialNumberResponse); @@ -567,19 +567,19 @@ void ApplicationLayer::domainAddressSerialNumberReadResponse(Priority priority, //TODO: ApplicationLayer::IndividualAddressSerialNumberWriteRequest() //TODO: ApplicationLayer::IndividualAddressSerialNumberReadRequest() -void ApplicationLayer::IndividualAddressSerialNumberReadResponse(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* rfDoA, +void ApplicationLayer::IndividualAddressSerialNumberReadResponse(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* domainAddress, const uint8_t* knxSerialNumber) { - CemiFrame frame(13); + CemiFrame frame(11); APDU& apdu = frame.apdu(); apdu.type(IndividualAddressSerialNumberResponse); uint8_t* data = apdu.data() + 1; memcpy(data, knxSerialNumber, 6); - memcpy(data + 6, rfDoA, 6); + memcpy(data + 6, domainAddress, 2); - //apdu.printPDU(); + //apdu.printPDU(); dataBroadcastRequest(AckDontCare, hopType, SystemPriority, apdu, secCtrl); } diff --git a/src/knx/application_layer.h b/src/knx/application_layer.h index bd14ef26..c7593c8c 100644 --- a/src/knx/application_layer.h +++ b/src/knx/application_layer.h @@ -154,7 +154,7 @@ class ApplicationLayer uint8_t* testResult, uint16_t testResultLength); void domainAddressSerialNumberReadResponse(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* rfDoA, const uint8_t* knxSerialNumber); - void IndividualAddressSerialNumberReadResponse(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* rfDoA, + void IndividualAddressSerialNumberReadResponse(Priority priority, HopCountType hopType, const SecurityControl& secCtrl, const uint8_t* domainAddress, const uint8_t* knxSerialNumber); #pragma endregion diff --git a/src/knx/bau27B0.cpp b/src/knx/bau27B0.cpp index 9b119f9b..6180539a 100644 --- a/src/knx/bau27B0.cpp +++ b/src/knx/bau27B0.cpp @@ -169,10 +169,7 @@ void Bau27B0::domainAddressSerialNumberReadIndication(Priority priority, HopCoun void Bau27B0::individualAddressSerialNumberReadIndication(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, uint8_t* knxSerialNumber) { - // If the received serial number matches our serial number - // then send a response with the current RF domain address stored in the RF medium object and the serial number - if (!memcmp(knxSerialNumber, _deviceObj.propertyData(PID_SERIAL_NUMBER), 6)) - _appLayer.IndividualAddressSerialNumberReadResponse(priority, hopType, secCtrl, _rfMediumObj.rfDomainAddress(), knxSerialNumber); + #pragma warning "individualAddressSerialNumberReadIndication is not available for rf" } void Bau27B0::domainAddressSerialNumberWriteLocalConfirm(Priority priority, HopCountType hopType, const SecurityControl &secCtrl, const uint8_t* rfDoA, diff --git a/src/knx/bau_systemB.cpp b/src/knx/bau_systemB.cpp index 9b8897fe..fc1fdf81 100644 --- a/src/knx/bau_systemB.cpp +++ b/src/knx/bau_systemB.cpp @@ -494,7 +494,7 @@ void BauSystemB::individualAddressSerialNumberReadIndication(Priority priority, // An open medium BAU has to override this method and provide a proper domain address. if (!memcmp(knxSerialNumber, _deviceObj.propertyData(PID_SERIAL_NUMBER), 6)) { - uint8_t emptyDomainAddress[6] = {0x00}; + uint8_t emptyDomainAddress[2] = {0x00}; applicationLayer().IndividualAddressSerialNumberReadResponse(priority, hopType, secCtrl, emptyDomainAddress, knxSerialNumber); } } diff --git a/src/knx/ip_data_link_layer.cpp b/src/knx/ip_data_link_layer.cpp index 43a7a595..8c51f790 100644 --- a/src/knx/ip_data_link_layer.cpp +++ b/src/knx/ip_data_link_layer.cpp @@ -70,9 +70,17 @@ void IpDataLinkLayer::loop() _platform.sendBytesUniCast(hpai.ipAddress(), hpai.ipPortNumber(), searchResponse.data(), searchResponse.totalLength()); break; } + case SearchRequestExt: + { + // FIXME, implement (not needed atm) + break; + } default: +#ifdef KNX_LOG_IP print("Unhandled service identifier: "); println(code, HEX); +#endif + ; } } diff --git a/src/knx/ip_parameter_object.cpp b/src/knx/ip_parameter_object.cpp index dcbbba35..3de44d34 100644 --- a/src/knx/ip_parameter_object.cpp +++ b/src/knx/ip_parameter_object.cpp @@ -34,8 +34,9 @@ IpParameterObject::IpParameterObject(DeviceObject& deviceObject, Platform& platf io->_deviceObject.individualAddress(getWord(data)); return 1; }), + new DataProperty(PID_CURRENT_IP_ASSIGNMENT_METHOD, false, PDT_UNSIGNED_CHAR, 0, ReadLv3 | WriteLv3), new DataProperty(PID_IP_ASSIGNMENT_METHOD, true, PDT_UNSIGNED_CHAR, 1, ReadLv3 | WriteLv3), - new DataProperty(PID_IP_CAPABILITIES, true, PDT_BITSET8, 1, ReadLv3 | WriteLv1), + new DataProperty(PID_IP_CAPABILITIES, true, PDT_BITSET8, 0, ReadLv3 | WriteLv1), // must be set by application due to capabilities of the used ip stack new CallbackProperty(this, PID_CURRENT_IP_ADDRESS, false, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv0, [](IpParameterObject* io, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t { diff --git a/src/knx/knx_ip_frame.h b/src/knx/knx_ip_frame.h index f8a421f5..c6c1c518 100644 --- a/src/knx/knx_ip_frame.h +++ b/src/knx/knx_ip_frame.h @@ -23,6 +23,8 @@ enum KnxIpServiceType ConnectionStateResponse = 0x208, DisconnectRequest = 0x209, DisconnectResponse = 0x20A, + SearchRequestExt = 0x20B, + SearchResponseExt = 0x20C, DeviceConfigurationRequest = 0x310, DeviceConfigurationAck = 0x311, TunnelingRequest = 0x420, diff --git a/src/knx_facade.cpp b/src/knx_facade.cpp index 1dee9fcc..03e4ce63 100644 --- a/src/knx_facade.cpp +++ b/src/knx_facade.cpp @@ -53,13 +53,17 @@ #error "Mask version not supported on ARDUINO_ARCH_SAMD" #endif #elif defined(ARDUINO_ARCH_RP2040) - // predefined global instance for TP or RF or TP/RF coupler + // predefined global instance for TP or RF or IP or TP/RF coupler or TP/IP coupler #if MASK_VERSION == 0x07B0 KnxFacade knx(buttonEvent); #elif MASK_VERSION == 0x27B0 KnxFacade knx(buttonEvent); + #elif MASK_VERSION == 0x57B0 + KnxFacade knx(buttonEvent); #elif MASK_VERSION == 0x2920 KnxFacade knx(buttonEvent); + #elif MASK_VERSION == 0x091A + KnxFacade knx(buttonEvent); #else #error "Mask version not supported on ARDUINO_ARCH_RP2040" #endif diff --git a/src/knx_facade.h b/src/knx_facade.h index 7facf61c..647b22be 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -480,13 +480,17 @@ template class KnxFacade : private SaveRestore #error "Mask version not supported on ARDUINO_ARCH_SAMD" #endif #elif defined(ARDUINO_ARCH_RP2040) - // predefined global instance for TP or RF or TP/RF coupler + // predefined global instance for TP or RF or TP/RF or TP/IP coupler #if MASK_VERSION == 0x07B0 extern KnxFacade knx; #elif MASK_VERSION == 0x27B0 extern KnxFacade knx; + #elif MASK_VERSION == 0x57B0 + extern KnxFacade knx; #elif MASK_VERSION == 0x2920 extern KnxFacade knx; + #elif MASK_VERSION == 0x091A + extern KnxFacade knx; #else #error "Mask version not supported on ARDUINO_ARCH_RP2040" #endif diff --git a/src/rp2040_arduino_platform.cpp b/src/rp2040_arduino_platform.cpp index 44eb8c4b..3443b77a 100644 --- a/src/rp2040_arduino_platform.cpp +++ b/src/rp2040_arduino_platform.cpp @@ -4,7 +4,7 @@ Plattform for Raspberry Pi Pico and other RP2040 boards by SirSydom 2021-2022 made to work with arduino-pico - "Raspberry Pi Pico Arduino core, for all RP2040 boards" -by Earl E. Philhower III https://github.com/earlephilhower/arduino-pico V1.11.0 +by Earl E. Philhower III https://github.com/earlephilhower/arduino-pico RTTI must be set to enabled in the board options @@ -17,6 +17,10 @@ EEPROM Emulation from arduino-pico core (max 4k) can be use by defining USE_RP20 A RAM-buffered Flash can be use by defining USE_RP2040_LARGE_EEPROM_EMULATION +For usage of KNX-IP you have to define either +- KNX_IP_W5500 (use the arduino-pico core's w5500 lwip stack) +- KNX_IP_WIFI (use the arduino-pico core's PiPicoW lwip stack) +- KNX_IP_GENERIC (use the Ethernet_Generic stack) ----------------------------------------------------*/ @@ -45,8 +49,11 @@ A RAM-buffered Flash can be use by defining USE_RP2040_LARGE_EEPROM_EMULATION #endif #endif -#ifndef KNX_SERIAL -#define KNX_SERIAL Serial1 +#ifdef KNX_IP_W5500 +extern Wiznet5500lwIP KNX_NETIF; +#elif defined(KNX_IP_WIFI) +#elif defined(KNX_IP_GENERIC) + #endif RP2040ArduinoPlatform::RP2040ArduinoPlatform() @@ -234,6 +241,125 @@ void RP2040ArduinoPlatform::writeBufferedEraseBlock() } } #endif + +#if defined(KNX_NETIF) +uint32_t RP2040ArduinoPlatform::currentIpAddress() +{ + return KNX_NETIF.localIP(); +} +uint32_t RP2040ArduinoPlatform::currentSubnetMask() +{ + return KNX_NETIF.subnetMask(); +} +uint32_t RP2040ArduinoPlatform::currentDefaultGateway() +{ + return KNX_NETIF.gatewayIP(); +} +void RP2040ArduinoPlatform::macAddress(uint8_t* addr) +{ +#if defined(KNX_IP_W5500) + addr = KNX_NETIF.getNetIf()->hwaddr; +#elif defined(KNX_IP_WIFI) + uint8_t macaddr[6] = {0,0,0,0,0,0}; + addr = KNX_NETIF.macAddress(macaddr); +#elif defined(KNX_IP_GENERIC) + KNX_NETIF.MACAddress(addr); +#endif +} + +// multicast +void RP2040ArduinoPlatform::setupMultiCast(uint32_t addr, uint16_t port) +{ + mcastaddr = IPAddress(htonl(addr)); + _port = port; + uint8_t result = _udp.beginMulticast(mcastaddr, port); + (void) result; + + #ifdef KNX_IP_GENERIC + //if(!_unicast_socket_setup) + // _unicast_socket_setup = UDP_UNICAST.begin(3671); + #endif + +#ifdef KNX_LOG_IP + print("Setup Mcast addr: "); + print(mcastaddr.toString().c_str()); + print(" on port: "); + print(port); + print(" result "); + println(result); +#endif +} + +void RP2040ArduinoPlatform::closeMultiCast() +{ + _udp.stop(); +} + +bool RP2040ArduinoPlatform::sendBytesMultiCast(uint8_t* buffer, uint16_t len) +{ +#ifdef KNX_LOG_IP + printHex("<- ",buffer, len); +#endif + //ToDo: check if Ethernet is able to receive + _udp.beginPacket(mcastaddr, _port); + _udp.write(buffer, len); + _udp.endPacket(); + return true; +} + +int RP2040ArduinoPlatform::readBytesMultiCast(uint8_t* buffer, uint16_t maxLen) +{ + int len = _udp.parsePacket(); + if (len == 0) + return 0; + + if (len > maxLen) + { + print("udp buffer to small. was "); + print(maxLen); + print(", needed "); + println(len); + fatalError(); + } + + _udp.read(buffer, len); +#ifdef KNX_LOG_IP + print("Remote IP: "); + print(_udp.remoteIP().toString().c_str()); + + printHex("-> ", buffer, len); +#endif + return len; +} + +// unicast +bool RP2040ArduinoPlatform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) +{ + IPAddress ucastaddr(htonl(addr)); + +#ifdef KNX_LOG_IP + print("sendBytesUniCast to:"); + println(ucastaddr.toString().c_str()); +#endif + +#ifdef KNX_IP_GENERIC + if(!_unicast_socket_setup) + _unicast_socket_setup = UDP_UNICAST.begin(3671); +#endif + + if (UDP_UNICAST.beginPacket(ucastaddr, port) == 1) + { + UDP_UNICAST.write(buffer, len); + if (UDP_UNICAST.endPacket() == 0) + println("sendBytesUniCast endPacket fail"); + } + else + println("sendBytesUniCast beginPacket fail"); + + return true; +} +#endif + #endif diff --git a/src/rp2040_arduino_platform.h b/src/rp2040_arduino_platform.h index 15e6dcb7..e6df5466 100644 --- a/src/rp2040_arduino_platform.h +++ b/src/rp2040_arduino_platform.h @@ -1,3 +1,5 @@ +#pragma once + #include "arduino_platform.h" #include "Arduino.h" @@ -15,6 +17,47 @@ #define USE_RP2040_EEPROM_EMULATION #endif +#ifndef KNX_SERIAL +#pragma warn "KNX_SERIAL not defined, using Serial1" +#define KNX_SERIAL Serial1 +#endif + +#ifdef KNX_IP_W5500 +#if ARDUINO_PICO_MAJOR * 10000 + ARDUINO_PICO_MINOR * 100 + ARDUINO_PICO_REVISION < 30600 +#pragma error "arduino-pico >= 3.6.0 needed" +#endif +#define KNX_NETIF Eth + +#include "SPI.h" +#include + +#elif defined(KNX_IP_WIFI) + +#define KNX_NETIF WiFi +#include + +#elif defined(KNX_IP_GENERIC) + + +#include + +#ifndef DEBUG_ETHERNET_GENERIC_PORT +#define DEBUG_ETHERNET_GENERIC_PORT Serial +#endif + +#ifndef _ETG_LOGLEVEL_ +#define _ETG_LOGLEVEL_ 1 +#endif + + +#define ETHERNET_USE_RPIPICO true +#include // https://github.com/khoih-prog/Ethernet_Generic + + +#define KNX_NETIF Ethernet + +#endif + class RP2040ArduinoPlatform : public ArduinoPlatform { @@ -55,6 +98,35 @@ class RP2040ArduinoPlatform : public ArduinoPlatform // writes _eraseblockBuffer to flash - overrides Plattform::writeBufferedEraseBlock() for performance optimization only void writeBufferedEraseBlock(); #endif + + + #if defined(KNX_NETIF) + uint32_t currentIpAddress() override; + uint32_t currentSubnetMask() override; + uint32_t currentDefaultGateway() override; + void macAddress(uint8_t* addr) override; + + // multicast + void setupMultiCast(uint32_t addr, uint16_t port) override; + void closeMultiCast() override; + bool sendBytesMultiCast(uint8_t* buffer, uint16_t len) override; + int readBytesMultiCast(uint8_t* buffer, uint16_t maxLen) override; + + // unicast + bool sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) override; + + #if defined(KNX_IP_W5500) || defined(KNX_IP_WIFI) + #define UDP_UNICAST _udp + protected: WiFiUDP _udp; + #elif defined(KNX_IP_GENERIC) + #define UDP_UNICAST _udp_uni + protected: bool _unicast_socket_setup = false; + protected: EthernetUDP _udp; + protected: EthernetUDP UDP_UNICAST; + #endif + protected: IPAddress mcastaddr; + protected: uint16_t _port; + #endif }; #endif