diff --git a/README.md b/README.md index f3b8f3e..e03c4e4 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # knx -This projects provides a knx-device stack for Arduino (ESP8266, ESP32, SAMD21, RP2040, STM32), CC1310, ESP IDF and Linux. (more are quite easy to add) +This projects provides a knx-device stack for Arduino (ESP8266, ESP32, SAMD21, RP2040, STM32), CC1310, ESP IDF, LibreTiny (BK7231, RTL8710 and LN882H) and Linux. (more are quite easy to add) It implements most of System-B specification and can be configured with ETS. The necessary knxprod-files can be generated with the [Kaenx-Creator](https://github.com/OpenKNX/Kaenx-Creator) tool. diff --git a/examples/knx-demo/knx-demo.ino b/examples/knx-demo/knx-demo.ino index a72b214..a9f6d84 100644 --- a/examples/knx-demo/knx-demo.ino +++ b/examples/knx-demo/knx-demo.ino @@ -58,7 +58,11 @@ void setup() Serial.begin(115200); ArduinoPlatform::SerialDebug = &Serial; +#ifdef LIBRETINY + srandom(millis()); +#else randomSeed(millis()); +#endif #if MASK_VERSION != 0x07B0 && (defined ARDUINO_ARCH_ESP8266 || defined ARDUINO_ARCH_ESP32) WiFiManager wifiManager; diff --git a/examples/knx-demo/platformio-ci.ini b/examples/knx-demo/platformio-ci.ini index b426eb0..701185a 100644 --- a/examples/knx-demo/platformio-ci.ini +++ b/examples/knx-demo/platformio-ci.ini @@ -96,3 +96,17 @@ build_flags = -DKNX_FLASH_SIZE=4096 -D PIO_FRAMEWORK_ARDUINO_ENABLE_RTTI -Wno-unknown-pragmas + +;--- LibreTiny BK7231N ------------------------------------ +[env:BK7231N_ip] +platform = libretiny +board = cbu +framework = arduino +lib_deps = + knx + +build_flags = + -DMASK_VERSION=0x57B0 + -DKNX_NO_SPI + -DKNX_FLASH_OFFSET=0x1DB000 + -Wno-unknown-pragmas \ No newline at end of file diff --git a/examples/knx-demo/platformio.ini b/examples/knx-demo/platformio.ini index 0de299b..9394219 100644 --- a/examples/knx-demo/platformio.ini +++ b/examples/knx-demo/platformio.ini @@ -168,4 +168,22 @@ build_flags = -DMASK_VERSION=0x07B0 -DKNX_FLASH_SIZE=4096 -D PIO_FRAMEWORK_ARDUINO_ENABLE_RTTI + -Wno-unknown-pragmas + +;--- LibreTiny BK7231N ------------------------------------ +[env:BK7231N_ip] +platform = libretiny +board = cbu +framework = arduino +; We consider that the this projects is opened within its project directory +; while working with VS Code. +lib_extra_dirs = ../../../ + +lib_deps = + knx + +build_flags = + -DMASK_VERSION=0x57B0 + -DKNX_NO_SPI + -DKNX_FLASH_OFFSET=0x1DB000 -Wno-unknown-pragmas \ No newline at end of file diff --git a/src/knx/bits.h b/src/knx/bits.h index 0764b55..6eee917 100644 --- a/src/knx/bits.h +++ b/src/knx/bits.h @@ -11,21 +11,13 @@ #define htonl(x) ( (getbyte(x,0)<<24) | (getbyte(x,1)<<16) | (getbyte(x,2)<<8) | getbyte(x,3) ) #define ntohs(x) htons(x) #define ntohl(x) htonl(x) +#elif defined(LIBRETINY) + #include + #define htons(x) lwip_htons(x) + #define htonl(x) lwip_htonl(x) #endif -#ifndef MIN - #define MIN(a, b) ((a < b) ? (a) : (b)) -#endif - -#ifndef MAX - #define MAX(a, b) ((a > b) ? (a) : (b)) -#endif - -#ifndef ABS - #define ABS(x) ((x > 0) ? (x) : (-x)) -#endif - -#if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_STM32) +#if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_RP2040) || defined(ARDUINO_ARCH_STM32) || defined(LIBRETINY) #include #elif defined(ARDUINO_ARCH_ESP8266) #include @@ -85,6 +77,18 @@ void attachInterrupt(uint32_t pin, voidFuncPtr callback, uint32_t mode); #endif +#ifndef MIN + #define MIN(a, b) ((a < b) ? (a) : (b)) +#endif + +#ifndef MAX + #define MAX(a, b) ((a > b) ? (a) : (b)) +#endif + +#ifndef ABS + #define ABS(x) ((x > 0) ? (x) : (-x)) +#endif + #ifndef KNX_NO_PRINT void print(const char[]); void print(char); diff --git a/src/knx/group_object.h b/src/knx/group_object.h index ca80155..cf65dae 100644 --- a/src/knx/group_object.h +++ b/src/knx/group_object.h @@ -21,7 +21,7 @@ enum ComFlag : uint8_t class GroupObject; #ifndef HAS_FUNCTIONAL - #if defined(__linux__) || defined(ARDUINO_ARCH_ESP32) || defined(ESP_PLATFORM) || defined(ARDUINO_ARCH_STM32) || defined (ARDUINO_ARCH_SAMD) || defined (ARDUINO_ARCH_RP2040) + #if defined(__linux__) || defined(ARDUINO_ARCH_ESP32) || defined(ESP_PLATFORM) || defined(ARDUINO_ARCH_STM32) || defined (ARDUINO_ARCH_SAMD) || defined (ARDUINO_ARCH_RP2040) || defined(LIBRETINY) #define HAS_FUNCTIONAL 1 #else #define HAS_FUNCTIONAL 0 diff --git a/src/knx_facade.cpp b/src/knx_facade.cpp index e2edd2b..6704b99 100644 --- a/src/knx_facade.cpp +++ b/src/knx_facade.cpp @@ -8,7 +8,8 @@ defined(ARDUINO_ARCH_ESP32) || \ defined(ARDUINO_ARCH_ESP8266) || \ defined(ARDUINO_ARCH_SAMD) || \ - defined(ARDUINO_ARCH_RP2040)) + defined(ARDUINO_ARCH_RP2040)) || \ + defined(LIBRETINY) // Only ESP8266 and ESP32 have this define. For all other platforms this is just empty. #ifndef IRAM_ATTR @@ -95,6 +96,18 @@ IRAM_ATTR void buttonEvent() #error "Mask version not supported on ARDUINO_ARCH_ESP32" #endif +#elif defined(LIBRETINY) + // predefined global instance for TP or IP or TP/IP coupler + #if MASK_VERSION == 0x07B0 + KnxFacade knx(buttonEvent); + #elif MASK_VERSION == 0x57B0 + KnxFacade knx(buttonEvent); + #elif MASK_VERSION == 0x091A + KnxFacade knx(buttonEvent); + #else + #error "Mask version not supported on LIBRETINY" + #endif + #elif defined(ESP_PLATFORM) // predefined global instance for TP or IP or TP/IP coupler #if MASK_VERSION == 0x07B0 diff --git a/src/knx_facade.h b/src/knx_facade.h index a897048..185ba64 100644 --- a/src/knx_facade.h +++ b/src/knx_facade.h @@ -32,6 +32,8 @@ #ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE void buttonUp(); #endif +#elif defined(LIBRETINY) + #include "libretiny_platform.h" #elif defined(ESP_PLATFORM) #include "esp32_idf_platform.h" #ifndef KNX_NO_AUTOMATIC_GLOBAL_INSTANCE @@ -265,7 +267,6 @@ template class KnxFacade : private SaveRestore void start() { - if (_ledPin >= 0) { #if defined(ESP_PLATFORM) @@ -553,6 +554,17 @@ template class KnxFacade : private SaveRestore #else #error "Mask version not supported on ARDUINO_ARCH_ESP32" #endif + #elif defined(LIBRETINY) + // predefined global instance for TP or IP or TP/IP coupler + #if MASK_VERSION == 0x07B0 + extern KnxFacade knx; + #elif MASK_VERSION == 0x57B0 + extern KnxFacade knx; + #elif MASK_VERSION == 0x091A + extern KnxFacade knx; + #else + #error "Mask version not supported on LIBRETINY" + #endif #elif defined(ESP_PLATFORM) // predefined global instance for TP or IP or TP/IP coupler #if MASK_VERSION == 0x07B0 diff --git a/src/libretiny_platform.cpp b/src/libretiny_platform.cpp new file mode 100644 index 0000000..f0a67f6 --- /dev/null +++ b/src/libretiny_platform.cpp @@ -0,0 +1,173 @@ +#include "libretiny_platform.h" + +#ifdef LIBRETINY +#include +#include "knx/bits.h" + +#include + +#ifndef KNX_SERIAL +#define KNX_SERIAL Serial +#endif + +#ifndef KNX_FLASH_OFFSET +#error "KNX_FLASH_OFFSET is not defined. E.g. 0x1DB000 for BK7231N" +#elif (KNX_FLASH_OFFSET % 4096) != 0 +#error "KNX_FLASH_OFFSET must be a multiple of 4096" +#endif + +static uint8_t NVS_buffer[KNX_FLASH_SIZE]; + +LibretinyPlatform::LibretinyPlatform() +#ifndef KNX_NO_DEFAULT_UART + : ArduinoPlatform(&KNX_SERIAL) +#endif +{ + _memoryType = Flash; +} + +LibretinyPlatform::LibretinyPlatform( HardwareSerial* s) : ArduinoPlatform(s) +{ + _memoryType = Flash; +} + +uint32_t LibretinyPlatform::currentIpAddress() +{ + return WiFi.localIP(); +} + +uint32_t LibretinyPlatform::currentSubnetMask() +{ + return WiFi.subnetMask(); +} + +uint32_t LibretinyPlatform::currentDefaultGateway() +{ + return WiFi.gatewayIP(); +} + +void LibretinyPlatform::macAddress(uint8_t * addr) +{ + WiFi.macAddress(addr); +} + +uint32_t LibretinyPlatform::uniqueSerialNumber() +{ + return lt_cpu_get_mac_id(); +} + +void LibretinyPlatform::restart() +{ + println("restart"); + lt_reboot(); +} + +void LibretinyPlatform::setupMultiCast(uint32_t addr, uint16_t port) +{ + //workaround for libretiny bug: NETIF_FLAG_IGMP is not set by default + struct netif *netif; + for (netif = netif_list; netif != NULL; netif = netif->next) + { + netif->flags |= NETIF_FLAG_IGMP; + } + + IPAddress mcastaddr(htonl(addr)); + KNX_DEBUG_SERIAL.printf("setup multicast addr: %d.%d.%d.%d port: %d ip: %d.%d.%d.%d\n", mcastaddr[0], mcastaddr[1], mcastaddr[2], mcastaddr[3], port, WiFi.localIP()[0], WiFi.localIP()[1], WiFi.localIP()[2], WiFi.localIP()[3]); + uint8_t result = _udp.beginMulticast(mcastaddr, port); + KNX_DEBUG_SERIAL.printf("multicast setup result %d\n", result); +} + +void LibretinyPlatform::closeMultiCast() +{ + _udp.stop(); +} + +bool LibretinyPlatform::sendBytesMultiCast(uint8_t * buffer, uint16_t len) +{ + _udp.beginMulticastPacket(); + _udp.write(buffer, len); + _udp.endPacket(); + return true; +} + +int LibretinyPlatform::readBytesMultiCast(uint8_t * buffer, uint16_t maxLen) +{ + int len = _udp.parsePacket(); + + if (len == 0) + return 0; + + if (len > maxLen) + { + KNX_DEBUG_SERIAL.printf("udp buffer to small. was %d, needed %d\n", maxLen, len); + fatalError(); + } + + _udp.read(buffer, len); + return len; +} + +bool LibretinyPlatform::sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) +{ + IPAddress ucastaddr(htonl(addr)); + println("sendBytesUniCast endPacket fail"); + + if(_udp.beginPacket(ucastaddr, port) == 1) + { + _udp.write(buffer, len); + + if(_udp.endPacket() == 0) + println("sendBytesUniCast endPacket fail"); + } + else + println("sendBytesUniCast beginPacket fail"); + + return true; +} + +size_t LibretinyPlatform::flashEraseBlockSize() +{ + return 16; +} + +size_t LibretinyPlatform::flashPageSize() +{ + return 256; +} + +uint8_t* LibretinyPlatform::userFlashStart() +{ + lt_flash_read(KNX_FLASH_OFFSET, NVS_buffer, KNX_FLASH_SIZE); + return NVS_buffer; +} + +size_t LibretinyPlatform::userFlashSizeEraseBlocks() +{ + if(KNX_FLASH_SIZE <= 0) + return 0; + else + return ( (KNX_FLASH_SIZE - 1) / (flashPageSize() * flashEraseBlockSize())) + 1; +} + +void LibretinyPlatform::flashErase(uint16_t eraseBlockNum) +{ + // 16 pages x 256byte/page = 4096byte + lt_flash_erase_block(KNX_FLASH_OFFSET + eraseBlockNum * flashPageSize() * flashEraseBlockSize()); +} + +void LibretinyPlatform::flashWritePage(uint16_t pageNumber, uint8_t* data) +{ + lt_flash_write(KNX_FLASH_OFFSET + pageNumber * flashPageSize(), data, flashPageSize()); +} + +void LibretinyPlatform::writeBufferedEraseBlock() +{ + if(_bufferedEraseblockNumber > -1 && _bufferedEraseblockDirty) + { + lt_flash_erase_block(KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize()); + lt_flash_write(KNX_FLASH_OFFSET + _bufferedEraseblockNumber * flashPageSize() * flashEraseBlockSize(), _eraseblockBuffer, flashPageSize() * flashEraseBlockSize()); + _bufferedEraseblockDirty = false; + } +} + +#endif diff --git a/src/libretiny_platform.h b/src/libretiny_platform.h new file mode 100644 index 0000000..dc62547 --- /dev/null +++ b/src/libretiny_platform.h @@ -0,0 +1,53 @@ +#ifdef LIBRETINY +#include "arduino_platform.h" + +#include +#include + +class LibretinyPlatform : public ArduinoPlatform +{ + public: + LibretinyPlatform(); + LibretinyPlatform(HardwareSerial* s); + + // ip stuff + uint32_t currentIpAddress() override; + uint32_t currentSubnetMask() override; + uint32_t currentDefaultGateway() override; + void macAddress(uint8_t* addr) override; + + // unique serial number + uint32_t uniqueSerialNumber() override; + + // basic stuff + void restart(); + + //multicast + void setupMultiCast(uint32_t addr, uint16_t port) override; + void closeMultiCast() override; + bool sendBytesMultiCast(uint8_t* buffer, uint16_t len) override; + int readBytesMultiCast(uint8_t* buffer, uint16_t maxLen) override; + + //unicast + bool sendBytesUniCast(uint32_t addr, uint16_t port, uint8_t* buffer, uint16_t len) override; + + // size of one EraseBlock in pages + virtual size_t flashEraseBlockSize(); + // size of one flash page in bytes + virtual size_t flashPageSize(); + // start of user flash aligned to start of an erase block + virtual uint8_t* userFlashStart(); + // size of the user flash in EraseBlocks + virtual size_t userFlashSizeEraseBlocks(); + // relativ to userFlashStart + virtual void flashErase(uint16_t eraseBlockNum); + // write a single page to flash (pageNumber relative to userFashStart + virtual void flashWritePage(uint16_t pageNumber, uint8_t* data); + + // writes _eraseblockBuffer to flash - overrides Plattform::writeBufferedEraseBlock() for performance optimization only + void writeBufferedEraseBlock(); + private: + WiFiUDP _udp; +}; + +#endif