Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add KNX-IP support for the rp2040 plattform #266

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions src/knx/application_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand All @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion src/knx/application_layer.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
5 changes: 1 addition & 4 deletions src/knx/bau27B0.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
2 changes: 1 addition & 1 deletion src/knx/bau_systemB.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down
8 changes: 8 additions & 0 deletions src/knx/ip_data_link_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we not clutter the code with different LOG_THIS, LOG_THAT, LOG_SOMETHING_ELSE? If I remember correctly DEBUG should already be used in the code. If there is more granular logic needed. There could be more levels like INFO, WARN, ERR or something.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the logging in the stack is very basic and has no levels. Its pain in the ass to debug specific stuff when you first have to comment in 100 lines.
That was the obvious, non breaking solution.
We (@traxanos and me) plan to rework this, with log levels AND specific settings that allow you to log only what you need.

print("Unhandled service identifier: ");
println(code, HEX);
#endif
;
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/knx/ip_parameter_object.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<IpParameterObject>(this, PID_CURRENT_IP_ADDRESS, false, PDT_UNSIGNED_LONG, 1, ReadLv3 | WriteLv0,
[](IpParameterObject* io, uint16_t start, uint8_t count, uint8_t* data) -> uint8_t
{
Expand Down
2 changes: 2 additions & 0 deletions src/knx/knx_ip_frame.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ enum KnxIpServiceType
ConnectionStateResponse = 0x208,
DisconnectRequest = 0x209,
DisconnectResponse = 0x20A,
SearchRequestExt = 0x20B,
SearchResponseExt = 0x20C,
DeviceConfigurationRequest = 0x310,
DeviceConfigurationAck = 0x311,
TunnelingRequest = 0x420,
Expand Down
6 changes: 5 additions & 1 deletion src/knx_facade.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<RP2040ArduinoPlatform, Bau07B0> knx(buttonEvent);
#elif MASK_VERSION == 0x27B0
KnxFacade<RP2040ArduinoPlatform, Bau27B0> knx(buttonEvent);
#elif MASK_VERSION == 0x57B0
KnxFacade<RP2040ArduinoPlatform, Bau57B0> knx(buttonEvent);
#elif MASK_VERSION == 0x2920
KnxFacade<RP2040ArduinoPlatform, Bau2920> knx(buttonEvent);
#elif MASK_VERSION == 0x091A
KnxFacade<RP2040ArduinoPlatform, Bau091A> knx(buttonEvent);
#else
#error "Mask version not supported on ARDUINO_ARCH_RP2040"
#endif
Expand Down
6 changes: 5 additions & 1 deletion src/knx_facade.h
Original file line number Diff line number Diff line change
Expand Up @@ -480,13 +480,17 @@ template <class P, class B> 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<RP2040ArduinoPlatform, Bau07B0> knx;
#elif MASK_VERSION == 0x27B0
extern KnxFacade<RP2040ArduinoPlatform, Bau27B0> knx;
#elif MASK_VERSION == 0x57B0
extern KnxFacade<RP2040ArduinoPlatform, Bau57B0> knx;
#elif MASK_VERSION == 0x2920
extern KnxFacade<RP2040ArduinoPlatform, Bau2920> knx;
#elif MASK_VERSION == 0x091A
extern KnxFacade<RP2040ArduinoPlatform, Bau091A> knx;
#else
#error "Mask version not supported on ARDUINO_ARCH_RP2040"
#endif
Expand Down
133 changes: 130 additions & 3 deletions src/rp2040_arduino_platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Plattform for Raspberry Pi Pico and other RP2040 boards
by SirSydom <[email protected]> 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
Expand All @@ -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)

----------------------------------------------------*/

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -234,6 +241,126 @@ 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


72 changes: 72 additions & 0 deletions src/rp2040_arduino_platform.h
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#pragma once

#include "arduino_platform.h"

#include "Arduino.h"
Expand All @@ -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 <W5500lwIP.h>

#elif defined(KNX_IP_WIFI)

#define KNX_NETIF WiFi
#include <WiFi.h>

#elif defined(KNX_IP_GENERIC)


#include <SPI.h>

#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 <Ethernet_Generic.hpp> // https://github.com/khoih-prog/Ethernet_Generic


#define KNX_NETIF Ethernet

#endif


class RP2040ArduinoPlatform : public ArduinoPlatform
{
Expand Down Expand Up @@ -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
Loading