Compare commits

...

2 Commits

Author SHA1 Message Date
Tony 0b2d00472e Enhance DALI Component Configuration and Functionality
- Updated README.md to include new configuration options for native timing values, TX/RX polarity, power-down polling, and logging levels.
- Introduced new default values for query response timeout and double-send delay in dali.c.
- Implemented a function to drain stale RX frames from the queue to improve query handling.
- Enhanced DALI HAL implementation in dali_hal_idf5.c with additional configuration options for timer resolution and bus power check intervals.
- Added logging capabilities to track bus states and message transmissions in the DALI HAL.
- Improved error handling and message response mechanisms in dali_domain.cpp and gateway_usb_setup.cpp for better communication reliability.
- Refactored GPIO handling to support configurable TX/RX active states in dali_hal.h.
- Introduced legacy query response handling for backward compatibility in the DALI domain.

Signed-off-by: Tony <tonylu@tony-cloud.com>
2026-05-15 01:26:13 +08:00
Tony 4553ed32e7 feat(gateway): add support for KNX TP UART 9-bit mode and enhance UART pin configuration
Signed-off-by: Tony <tonylu@tony-cloud.com>
2026-05-14 22:39:14 +08:00
21 changed files with 3942 additions and 127 deletions
+1 -1
View File
@@ -34,7 +34,7 @@ The native rewrite now wires a shared `gateway_core` bootstrap component, a mult
KNX Data Secure and KNXnet/IP Secure support are controlled by `GATEWAY_KNX_DATA_SECURE_SUPPORTED` and `GATEWAY_KNX_IP_SECURE_SUPPORTED`. The current KNXnet/IP Secure flag reserves and reports secure service capability, while runtime secure-session transport is still reported as not implemented until that path is wired. The gateway derives its KNX serial identity from the ESP base MAC, and the development factory setup key is deterministically derived from that KNX serial so the same board keeps the same FDSK across NVS erases.
The KNXnet/IP tunnel can start from the built-in default configuration before any ETS download. KNX TP-UART is enabled only when `GATEWAY_KNX_TP_UART_PORT` is `0`, `1`, or `2`; set that UART port to `-1` for IP-only operation. UART TX/RX GPIO values of `-1` mean use the ESP-IDF default IO routing for that UART, not disabled. Non-UART GPIO options use `-1` as disabled, including the KNX programming button, KNX programming LED, setup AP button, Wi-Fi reset button, and status LED.
The KNXnet/IP tunnel can start from the built-in default configuration before any ETS download. KNX TP-UART is enabled only when `GATEWAY_KNX_TP_UART_PORT` is `0`, `1`, or `2`; set that UART port to `-1` for IP-only operation. UART TX/RX GPIO values of `-1` mean use the ESP-IDF target default pins for that UART, not disabled. `GATEWAY_KNX_TP_UART_9BIT_MODE` enables the NCN5120/OpenKNX-style 9-bit host frame on the wire, represented on ESP-IDF as 8 data bits plus even parity. Non-UART GPIO options use `-1` as disabled, including the KNX programming button, KNX programming LED, setup AP button, Wi-Fi reset button, and status LED.
When no KNX bridge config or ETS application data has been downloaded, the KNXnet/IP router starts in commissioning mode: OpenKNX receives tunnel programming traffic from ETS, while DALI group routing and REG1-Dali function-property actions stay inactive until ETS reports a configured application.
+10
View File
@@ -772,6 +772,16 @@ config GATEWAY_KNX_TP_BAUDRATE
range 1200 921600
default 19200
config GATEWAY_KNX_TP_UART_9BIT_MODE
bool "KNX TP UART 9-bit mode"
depends on GATEWAY_KNX_BRIDGE_SUPPORTED
default y
help
Enable the NCN5120/OpenKNX-style 9-bit UART frame on the wire. ESP-IDF
exposes this as 8 data bits plus even parity, matching the TP-UART host
mode commonly described as 19200 baud 9-bit UART. Disable only for
hardware wired for 8N1 host UART mode.
config GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE
int "KNX/IP bridge task stack bytes"
depends on GATEWAY_KNX_BRIDGE_SUPPORTED
+5
View File
@@ -886,6 +886,11 @@ extern "C" void app_main(void) {
default_knx.tp_uart.tx_pin = CONFIG_GATEWAY_KNX_TP_TX_PIN;
default_knx.tp_uart.rx_pin = CONFIG_GATEWAY_KNX_TP_RX_PIN;
default_knx.tp_uart.baudrate = static_cast<uint32_t>(CONFIG_GATEWAY_KNX_TP_BAUDRATE);
#ifdef CONFIG_GATEWAY_KNX_TP_UART_9BIT_MODE
default_knx.tp_uart.nine_bit_mode = true;
#else
default_knx.tp_uart.nine_bit_mode = false;
#endif
bridge_config.default_knx_config = default_knx;
}
bridge_config.knx_task_stack_size =
+31 -13
View File
@@ -603,15 +603,13 @@ CONFIG_GATEWAY_CHANNEL_COUNT=1
#
CONFIG_GATEWAY_CHANNEL1_GW_ID=3
# CONFIG_GATEWAY_CHANNEL1_PHY_DISABLED is not set
# CONFIG_GATEWAY_CHANNEL1_PHY_NATIVE is not set
CONFIG_GATEWAY_CHANNEL1_PHY_UART1=y
CONFIG_GATEWAY_CHANNEL1_PHY_NATIVE=y
# CONFIG_GATEWAY_CHANNEL1_PHY_UART1 is not set
# CONFIG_GATEWAY_CHANNEL1_PHY_UART2 is not set
CONFIG_GATEWAY_CHANNEL1_SERIAL_TX_PIN=1
CONFIG_GATEWAY_CHANNEL1_SERIAL_RX_PIN=2
CONFIG_GATEWAY_CHANNEL1_SERIAL_BAUDRATE=9600
CONFIG_GATEWAY_CHANNEL1_SERIAL_RX_BUFFER=512
CONFIG_GATEWAY_CHANNEL1_SERIAL_TX_BUFFER=512
CONFIG_GATEWAY_CHANNEL1_SERIAL_QUERY_TIMEOUT_MS=100
CONFIG_GATEWAY_CHANNEL1_NATIVE_BUS_ID=0
CONFIG_GATEWAY_CHANNEL1_NATIVE_TX_PIN=2
CONFIG_GATEWAY_CHANNEL1_NATIVE_RX_PIN=1
CONFIG_GATEWAY_CHANNEL1_NATIVE_BAUDRATE=1200
# end of Gateway Channel 1
#
@@ -626,7 +624,7 @@ CONFIG_GATEWAY_CACHE_SUPPORTED=y
CONFIG_GATEWAY_CACHE_START_ENABLED=y
CONFIG_GATEWAY_CACHE_RECONCILIATION_ENABLED=y
# CONFIG_GATEWAY_CACHE_FULL_STATE_MIRROR is not set
CONFIG_GATEWAY_CACHE_FLUSH_INTERVAL_MS=5000
CONFIG_GATEWAY_CACHE_FLUSH_INTERVAL_MS=60000
CONFIG_GATEWAY_CACHE_OUTSIDE_BUS_FIRST=y
# CONFIG_GATEWAY_CACHE_LOCAL_GATEWAY_FIRST is not set
# end of Gateway Cache
@@ -696,6 +694,7 @@ CONFIG_GATEWAY_KNX_TP_UART_PORT=0
CONFIG_GATEWAY_KNX_TP_TX_PIN=-1
CONFIG_GATEWAY_KNX_TP_RX_PIN=-1
CONFIG_GATEWAY_KNX_TP_BAUDRATE=19200
CONFIG_GATEWAY_KNX_TP_UART_9BIT_MODE=y
CONFIG_GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE=12288
CONFIG_GATEWAY_BRIDGE_KNX_TASK_PRIORITY=5
CONFIG_GATEWAY_CLOUD_BRIDGE_SUPPORTED=y
@@ -2772,13 +2771,32 @@ CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
#
CONFIG_DALI_PHY_COUNT=16
CONFIG_DALI_DEFAULT_BAUDRATE=1200
CONFIG_DALI_API_QUEUE_LEN=10
CONFIG_DALI_TX_QUEUE_LEN=1
CONFIG_DALI_TIMER_RESOLUTION_HZ=3000000
CONFIG_DALI_CUSTOM_HALF_BIT_TIME_X100_US=0
CONFIG_DALI_TX_STOP_CONDITION_US=0
CONFIG_DALI_RX_STOP_CONDITION_US=0
CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS=25
CONFIG_DALI_DOUBLE_SEND_DELAY_MS=12
CONFIG_DALI_BUS_POWER_CHECK_INTERVAL_MS=500
CONFIG_DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS=3000
# CONFIG_DALI_LOG_LEVEL_NONE is not set
# CONFIG_DALI_LOG_LEVEL_ERROR is not set
# CONFIG_DALI_LOG_LEVEL_WARN is not set
# CONFIG_DALI_LOG_LEVEL_INFO is not set
# CONFIG_DALI_LOG_LEVEL_DEBUG is not set
CONFIG_DALI_LOG_LEVEL_VERBOSE=y
CONFIG_DALI_LOG_LEVEL=5
CONFIG_DALI_TX_ACTIVE_LOW=y
# CONFIG_DALI_TX_ACTIVE_HIGH is not set
CONFIG_DALI_RX_ACTIVE_LOW=y
# CONFIG_DALI_RX_ACTIVE_HIGH is not set
CONFIG_DALI_API_QUEUE_LEN=64
CONFIG_DALI_TX_QUEUE_LEN=4
CONFIG_DALI_TX_REPLY_QUEUE_LEN=4
CONFIG_DALI_RX_QUEUE_LEN=50
CONFIG_DALI_DEBUG_QUEUE_LEN=100
# CONFIG_DALI_ENABLE_DEBUG_TASK is not set
CONFIG_DALI_DALI_TASK_STACK_SIZE=2048
CONFIG_DALI_ENABLE_DEBUG_TASK=y
CONFIG_DALI_DALI_TASK_STACK_SIZE=8192
CONFIG_DALI_DALI_TASK_PRIORITY=2
CONFIG_DALI_DEBUG_TASK_STACK_SIZE=2048
CONFIG_DALI_DEBUG_TASK_PRIORITY=1
File diff suppressed because it is too large Load Diff
+33 -26
View File
@@ -596,38 +596,25 @@ CONFIG_PARTITION_TABLE_MD5=y
#
# Gateway App
#
CONFIG_GATEWAY_CHANNEL_COUNT=2
CONFIG_GATEWAY_CHANNEL_COUNT=1
#
# Gateway Channel 1
#
CONFIG_GATEWAY_CHANNEL1_GW_ID=3
# CONFIG_GATEWAY_CHANNEL1_PHY_DISABLED is not set
# CONFIG_GATEWAY_CHANNEL1_PHY_NATIVE is not set
CONFIG_GATEWAY_CHANNEL1_PHY_UART1=y
CONFIG_GATEWAY_CHANNEL1_PHY_NATIVE=y
# CONFIG_GATEWAY_CHANNEL1_PHY_UART1 is not set
# CONFIG_GATEWAY_CHANNEL1_PHY_UART2 is not set
CONFIG_GATEWAY_CHANNEL1_SERIAL_TX_PIN=1
CONFIG_GATEWAY_CHANNEL1_SERIAL_RX_PIN=2
CONFIG_GATEWAY_CHANNEL1_SERIAL_BAUDRATE=9600
CONFIG_GATEWAY_CHANNEL1_SERIAL_RX_BUFFER=512
CONFIG_GATEWAY_CHANNEL1_SERIAL_TX_BUFFER=512
CONFIG_GATEWAY_CHANNEL1_SERIAL_QUERY_TIMEOUT_MS=100
CONFIG_GATEWAY_CHANNEL1_NATIVE_BUS_ID=0
CONFIG_GATEWAY_CHANNEL1_NATIVE_TX_PIN=2
CONFIG_GATEWAY_CHANNEL1_NATIVE_RX_PIN=1
CONFIG_GATEWAY_CHANNEL1_NATIVE_BAUDRATE=1200
# end of Gateway Channel 1
#
# Gateway Channel 2
#
CONFIG_GATEWAY_CHANNEL2_GW_ID=4
# CONFIG_GATEWAY_CHANNEL2_PHY_DISABLED is not set
# CONFIG_GATEWAY_CHANNEL2_PHY_NATIVE is not set
# CONFIG_GATEWAY_CHANNEL2_PHY_UART1 is not set
CONFIG_GATEWAY_CHANNEL2_PHY_UART2=y
CONFIG_GATEWAY_CHANNEL2_SERIAL_TX_PIN=6
CONFIG_GATEWAY_CHANNEL2_SERIAL_RX_PIN=7
CONFIG_GATEWAY_CHANNEL2_SERIAL_BAUDRATE=9600
CONFIG_GATEWAY_CHANNEL2_SERIAL_RX_BUFFER=512
CONFIG_GATEWAY_CHANNEL2_SERIAL_TX_BUFFER=512
CONFIG_GATEWAY_CHANNEL2_SERIAL_QUERY_TIMEOUT_MS=100
# end of Gateway Channel 2
#
@@ -637,7 +624,7 @@ CONFIG_GATEWAY_CACHE_SUPPORTED=y
CONFIG_GATEWAY_CACHE_START_ENABLED=y
CONFIG_GATEWAY_CACHE_RECONCILIATION_ENABLED=y
# CONFIG_GATEWAY_CACHE_FULL_STATE_MIRROR is not set
CONFIG_GATEWAY_CACHE_FLUSH_INTERVAL_MS=5000
CONFIG_GATEWAY_CACHE_FLUSH_INTERVAL_MS=60000
CONFIG_GATEWAY_CACHE_OUTSIDE_BUS_FIRST=y
# CONFIG_GATEWAY_CACHE_LOCAL_GATEWAY_FIRST is not set
# end of Gateway Cache
@@ -707,7 +694,8 @@ CONFIG_GATEWAY_KNX_TP_UART_PORT=0
CONFIG_GATEWAY_KNX_TP_TX_PIN=-1
CONFIG_GATEWAY_KNX_TP_RX_PIN=-1
CONFIG_GATEWAY_KNX_TP_BAUDRATE=19200
CONFIG_GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE=8192
CONFIG_GATEWAY_KNX_TP_UART_9BIT_MODE=y
CONFIG_GATEWAY_BRIDGE_KNX_TASK_STACK_SIZE=12288
CONFIG_GATEWAY_BRIDGE_KNX_TASK_PRIORITY=5
CONFIG_GATEWAY_CLOUD_BRIDGE_SUPPORTED=y
# CONFIG_GATEWAY_START_CLOUD_BRIDGE_ENABLED is not set
@@ -1818,7 +1806,7 @@ CONFIG_ESP_SYSTEM_MEMPROT_FEATURE_LOCK=y
CONFIG_ESP_SYSTEM_EVENT_QUEUE_SIZE=32
CONFIG_ESP_SYSTEM_EVENT_TASK_STACK_SIZE=2304
CONFIG_ESP_MAIN_TASK_STACK_SIZE=3584
CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192
CONFIG_ESP_MAIN_TASK_AFFINITY_CPU0=y
# CONFIG_ESP_MAIN_TASK_AFFINITY_CPU1 is not set
# CONFIG_ESP_MAIN_TASK_AFFINITY_NO_AFFINITY is not set
@@ -2783,13 +2771,32 @@ CONFIG_WIFI_PROV_STA_ALL_CHANNEL_SCAN=y
#
CONFIG_DALI_PHY_COUNT=16
CONFIG_DALI_DEFAULT_BAUDRATE=1200
CONFIG_DALI_API_QUEUE_LEN=10
CONFIG_DALI_TX_QUEUE_LEN=1
CONFIG_DALI_TIMER_RESOLUTION_HZ=3000000
CONFIG_DALI_CUSTOM_HALF_BIT_TIME_X100_US=0
CONFIG_DALI_TX_STOP_CONDITION_US=0
CONFIG_DALI_RX_STOP_CONDITION_US=0
CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS=25
CONFIG_DALI_DOUBLE_SEND_DELAY_MS=12
CONFIG_DALI_BUS_POWER_CHECK_INTERVAL_MS=500
CONFIG_DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS=3000
# CONFIG_DALI_LOG_LEVEL_NONE is not set
# CONFIG_DALI_LOG_LEVEL_ERROR is not set
# CONFIG_DALI_LOG_LEVEL_WARN is not set
# CONFIG_DALI_LOG_LEVEL_INFO is not set
# CONFIG_DALI_LOG_LEVEL_DEBUG is not set
CONFIG_DALI_LOG_LEVEL_VERBOSE=y
CONFIG_DALI_LOG_LEVEL=5
CONFIG_DALI_TX_ACTIVE_LOW=y
# CONFIG_DALI_TX_ACTIVE_HIGH is not set
CONFIG_DALI_RX_ACTIVE_LOW=y
# CONFIG_DALI_RX_ACTIVE_HIGH is not set
CONFIG_DALI_API_QUEUE_LEN=64
CONFIG_DALI_TX_QUEUE_LEN=4
CONFIG_DALI_TX_REPLY_QUEUE_LEN=4
CONFIG_DALI_RX_QUEUE_LEN=50
CONFIG_DALI_DEBUG_QUEUE_LEN=100
# CONFIG_DALI_ENABLE_DEBUG_TASK is not set
CONFIG_DALI_DALI_TASK_STACK_SIZE=2048
CONFIG_DALI_DALI_TASK_STACK_SIZE=8192
CONFIG_DALI_DALI_TASK_PRIORITY=2
CONFIG_DALI_DEBUG_TASK_STACK_SIZE=2048
CONFIG_DALI_DEBUG_TASK_PRIORITY=1
+135 -1
View File
@@ -14,6 +14,140 @@ config DALI_DEFAULT_BAUDRATE
help
Default baudrate used during initialization.
config DALI_TIMER_RESOLUTION_HZ
int "Native DALI timer resolution Hz"
range 1000000 8000000
default 3000000
help
GPTimer resolution for native DALI Manchester timing. The default 3 MHz
allows a 1200 bps half-bit period of 416.67 us to be represented as
1250 timer ticks.
config DALI_CUSTOM_HALF_BIT_TIME_X100_US
int "Custom native DALI half-bit time x100 us"
range 0 500000
default 0
help
Development override for the native DALI half-bit period, expressed in
1/100 us. Set to 0 to derive the value from the configured baudrate.
Standard 1200 bps DALI is 41667, meaning 416.67 us.
config DALI_TX_STOP_CONDITION_US
int "Custom TX stop condition us"
range 0 10000
default 0
help
Development override for the native TX stop-condition wait. Set to 0 to
use the scaled standard timing.
config DALI_RX_STOP_CONDITION_US
int "Custom RX stop condition us"
range 0 10000
default 0
help
Development override for the native RX stop-condition wait. Set to 0 to
use the scaled standard timing.
config DALI_QUERY_RESPONSE_TIMEOUT_MS
int "DALI query response timeout ms"
range 10 100
default 25
help
Time to wait for a complete backward frame after a forward query has
finished transmitting. DALI backward frames start 5.5-10.5 ms after the
forward frame and last about 9.95 ms, so 25 ms leaves margin without the
legacy 50 ms no-response delay.
config DALI_DOUBLE_SEND_DELAY_MS
int "Double-send delay ms"
range 0 100
default 10
help
Delay between the two frames sent by dali_send_double(), measured after
the first frame has completed. Exposed for development tuning.
config DALI_BUS_POWER_CHECK_INTERVAL_MS
int "Bus power-down check interval ms"
range 10 5000
default 100
help
Interval used to resample the RX pin while the native DALI bus is marked
power-down. This lets the HAL recover when the bus was already powered
before gateway startup and no RX edge is generated.
config DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS
int "Legacy bus abnormal report interval ms"
range 0 60000
default 1000
help
Interval for publishing the legacy two-byte FF FD bus-abnormal raw frame
while the native DALI bus is power-down. Set to 0 to disable the report.
choice DALI_LOG_LEVEL_CHOICE
prompt "DALI log level"
default DALI_LOG_LEVEL_WARN
help
Runtime log level applied to the native DALI HAL ESP-IDF log tag.
config DALI_LOG_LEVEL_NONE
bool "No output"
config DALI_LOG_LEVEL_ERROR
bool "Error"
config DALI_LOG_LEVEL_WARN
bool "Warning"
config DALI_LOG_LEVEL_INFO
bool "Info"
config DALI_LOG_LEVEL_DEBUG
bool "Debug"
config DALI_LOG_LEVEL_VERBOSE
bool "Verbose"
endchoice
config DALI_LOG_LEVEL
int
default 0 if DALI_LOG_LEVEL_NONE
default 1 if DALI_LOG_LEVEL_ERROR
default 2 if DALI_LOG_LEVEL_WARN
default 3 if DALI_LOG_LEVEL_INFO
default 4 if DALI_LOG_LEVEL_DEBUG
default 5 if DALI_LOG_LEVEL_VERBOSE
choice DALI_TX_ACTIVE_LEVEL
prompt "DALI TX pin active level"
default DALI_TX_ACTIVE_LOW
help
Select the physical GPIO level that drives the DALI bus active. The
native gateway default is TX active low.
config DALI_TX_ACTIVE_LOW
bool "Active low"
config DALI_TX_ACTIVE_HIGH
bool "Active high"
endchoice
choice DALI_RX_ACTIVE_LEVEL
prompt "DALI RX pin active level"
default DALI_RX_ACTIVE_HIGH
help
Select the physical GPIO level read when the DALI bus is active. The
native gateway default is RX active high.
config DALI_RX_ACTIVE_LOW
bool "Active low"
config DALI_RX_ACTIVE_HIGH
bool "Active high"
endchoice
config DALI_API_QUEUE_LEN
int "Global API queue length"
range 1 64
@@ -48,7 +182,7 @@ config DALI_ENABLE_DEBUG_TASK
config DALI_DALI_TASK_STACK_SIZE
int "DALI task stack size"
range 1024 8192
default 2048
default 4096
config DALI_DALI_TASK_PRIORITY
int "DALI task priority"
+16
View File
@@ -41,10 +41,26 @@ void app_main(void) {
Use `menuconfig` under `DALI Component` to configure:
- Bus count and default baudrate.
- Native timing values for development, including timer resolution, half-bit period,
TX/RX stop conditions, query response timeout, and double-send delay.
- TX/RX active polarity. The native gateway default is TX active low and RX active high.
- Native bus power-down polling and legacy `FF FD` bus-abnormal raw-frame reporting intervals.
- Native DALI HAL log level for the `dali_hal` ESP-IDF log tag.
- Queue sizes.
- Task stack sizes and priorities.
- Optional debug task.
The native bus monitor uses `CONFIG_DALI_BUS_POWER_CHECK_INTERVAL_MS` to resample RX while
power-down and `CONFIG_DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS` to publish legacy `FF FD`
raw frames while down. The report interval defaults to 1000 ms; set it to 0 to disable the
compatibility report.
Native timing defaults target standard 1200 bps DALI: a 416.67 us half-bit period is
generated by the default 3 MHz timer as 1250 ticks. `CONFIG_DALI_CUSTOM_HALF_BIT_TIME_X100_US`
can override the half-bit period for development; keep it at 0 for baudrate-derived timing.
Query no-response timeout defaults to 25 ms, which covers the 5.5-10.5 ms backward-frame
start window plus the approximately 9.95 ms backward frame duration.
## API Note
The global TX response queue symbol was renamed:
+28 -7
View File
@@ -6,6 +6,14 @@
#include <memory.h> // for memset
#include "freertos/semphr.h"
#ifndef CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS
#define CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS 25
#endif
#ifndef CONFIG_DALI_DOUBLE_SEND_DELAY_MS
#define CONFIG_DALI_DOUBLE_SEND_DELAY_MS 10
#endif
static SemaphoreHandle_t s_dali_core_lock;
static SemaphoreHandle_t dali_core_mutex(void)
@@ -31,6 +39,20 @@ static inline void dali_core_unlock(void)
}
}
static UBaseType_t drain_rx_queue(QueueHandle_t rx_q)
{
if (rx_q == NULL) {
return 0;
}
Dali_msg_t stale = {0};
UBaseType_t drained = 0;
while (xQueueReceive(rx_q, &stale, 0) == pdTRUE) {
drained++;
}
return drained;
}
Dali_msg_t dali_msg_new_generic(uint8_t bit_length, uint8_t address, uint8_t cmd1, uint8_t cmd2, uint8_t cmd3) {
Dali_msg_t dali_msg;
dali_msg.id = 0;
@@ -90,7 +112,7 @@ void dali_send_double(Dali_msg_t *dali_msg) {
dali_send_locked(dali_msg);
// TODO check status
dali_msg->id++; // increment message ID
dali_delay_ms(10); // delay 13ms 101.8.1.2: 13.5 - 75ms
dali_delay_ms(CONFIG_DALI_DOUBLE_SEND_DELAY_MS);
dali_send_locked(dali_msg);
// TODO check status
dali_core_unlock();
@@ -110,17 +132,16 @@ int dali_query(Dali_msg_t *tx_msg, Dali_msg_t *rx_msg) {
return -1;
}
// TODO check empty queue
if(xQueueReceive(rx_q, rx_msg, 0) == pdTRUE) {
printf("Queue not empty\n");
return -1;
dali_core_lock();
UBaseType_t drained = drain_rx_queue(rx_q);
if (drained > 0) {
printf("dali_query: dropped %u stale RX frame(s)\n", (unsigned)drained);
}
// printf("check A tx=%d tm=%d[ms] st=%d len=%d d0=0x%X\n", ret, tx_msg->type, tx_msg->status, tx_msg->length, tx_msg->data[0]);
dali_core_lock();
dali_send_locked(tx_msg);
// receive message from DALI task
ret = xQueueReceive(rx_q, rx_msg, pdMS_TO_TICKS(50));
ret = xQueueReceive(rx_q, rx_msg, pdMS_TO_TICKS(CONFIG_DALI_QUERY_RESPONSE_TIMEOUT_MS));
dali_core_unlock();
// printf("B rx=%d tm=%d[ms] st=%d len=%d d0=0x%X\n", ret, rx_msg->type, rx_msg->status, rx_msg->length, rx_msg->data[0]);
return ret;
+420 -44
View File
@@ -4,6 +4,15 @@
//ESP-IDF HAL
#ifdef CONFIG_IDF_TARGET
#include "sdkconfig.h"
#ifndef CONFIG_DALI_LOG_LEVEL
#define CONFIG_DALI_LOG_LEVEL 2
#endif
#ifndef LOG_LOCAL_LEVEL
#define LOG_LOCAL_LEVEL CONFIG_DALI_LOG_LEVEL
#endif
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
@@ -39,7 +48,7 @@
#endif
#ifndef CONFIG_DALI_DALI_TASK_STACK_SIZE
#define CONFIG_DALI_DALI_TASK_STACK_SIZE 2048
#define CONFIG_DALI_DALI_TASK_STACK_SIZE 4096
#endif
#ifndef CONFIG_DALI_DALI_TASK_PRIORITY
@@ -54,14 +63,41 @@
#define CONFIG_DALI_DEBUG_TASK_PRIORITY 1
#endif
#ifndef CONFIG_DALI_TIMER_RESOLUTION_HZ
#define CONFIG_DALI_TIMER_RESOLUTION_HZ 3000000
#endif
#ifndef CONFIG_DALI_CUSTOM_HALF_BIT_TIME_X100_US
#define CONFIG_DALI_CUSTOM_HALF_BIT_TIME_X100_US 0
#endif
#ifndef CONFIG_DALI_TX_STOP_CONDITION_US
#define CONFIG_DALI_TX_STOP_CONDITION_US 0
#endif
#ifndef CONFIG_DALI_RX_STOP_CONDITION_US
#define CONFIG_DALI_RX_STOP_CONDITION_US 0
#endif
#ifndef CONFIG_DALI_BUS_POWER_CHECK_INTERVAL_MS
#define CONFIG_DALI_BUS_POWER_CHECK_INTERVAL_MS 100
#endif
#ifndef CONFIG_DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS
#define CONFIG_DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS 1000
#endif
#define WITHIN_RANGE(x, min, max) ((x) > (min) && (x) < (max))
#define MAX_DELTA_RELOAD_TIME 600000000 // 600s - max u32: 4,294,967,295~4,294s
#define DALI_BAUDRATE_MIN 400U
#define DALI_BAUDRATE_MAX 2400U
#define DALI_BUS_POWER_CHECK_INTERVAL_US ((uint64_t)CONFIG_DALI_BUS_POWER_CHECK_INTERVAL_MS * 1000ULL)
#define DALI_BUS_ABNORMAL_REPORT_INTERVAL_US ((uint64_t)CONFIG_DALI_BUS_ABNORMAL_REPORT_INTERVAL_MS * 1000ULL)
typedef struct {
uint32_t hb;
uint32_t timer_alarm_ticks;
uint32_t rx_hb_min;
uint32_t rx_hb_max;
uint32_t rx_2hb_min;
@@ -76,6 +112,11 @@ typedef struct {
static const char *TAG = "dali_hal";
static inline void apply_dali_log_level(void)
{
esp_log_level_set(TAG, (esp_log_level_t)CONFIG_DALI_LOG_LEVEL);
}
typedef struct {
uint8_t bus_id;
uint8_t tx_pin;
@@ -91,6 +132,10 @@ typedef struct {
uint32_t rx_tx_delta;
uint32_t rx_pulse_width;
uint8_t rx_level;
uint8_t tx_level;
uint64_t bus_level_check_time;
uint64_t bus_abnormal_report_time;
Dali_msg_t tx_data;
Dali_msg_t rx_data;
@@ -137,17 +182,31 @@ static inline void dali_hal_unlock(void)
}
}
static uint32_t dali_half_bit_from_baud(uint32_t baudrate)
static uint32_t dali_half_bit_x100_from_baud(uint32_t baudrate)
{
if (baudrate < DALI_BAUDRATE_MIN || baudrate > DALI_BAUDRATE_MAX) {
return 0;
}
uint64_t hb = 500000ULL + (baudrate / 2U); // round to nearest
hb /= baudrate;
if (hb == 0 || hb > 2000000ULL) { // should never happen with checked bounds
uint64_t hb_x100 = 50000000ULL + (baudrate / 2U); // round to nearest 0.01 us
hb_x100 /= baudrate;
if (hb_x100 == 0 || hb_x100 > 200000000ULL) { // should never happen with checked bounds
return 0;
}
return (uint32_t)hb;
return (uint32_t)hb_x100;
}
static uint32_t half_bit_us_from_x100(uint32_t hb_x100)
{
return (hb_x100 + 50U) / 100U;
}
static uint32_t timer_alarm_ticks_from_x100(uint32_t hb_x100)
{
uint64_t ticks = ((uint64_t)CONFIG_DALI_TIMER_RESOLUTION_HZ * hb_x100 + 50000000ULL) / 100000000ULL;
if (ticks == 0 || ticks > UINT32_MAX) {
return 0;
}
return (uint32_t)ticks;
}
static inline uint32_t scale_time_by_hb(uint32_t base_us, uint32_t hb_us)
@@ -155,25 +214,40 @@ static inline uint32_t scale_time_by_hb(uint32_t base_us, uint32_t hb_us)
return (uint32_t)(((uint64_t)base_us * hb_us + (DALI_TIME_HB / 2U)) / DALI_TIME_HB);
}
static inline uint32_t configured_or_scaled_time(uint32_t configured_us, uint32_t base_us,
uint32_t hb_us)
{
return configured_us > 0 ? configured_us : scale_time_by_hb(base_us, hb_us);
}
static esp_err_t update_timing_locked(uint32_t baudrate)
{
uint32_t hb = dali_half_bit_from_baud(baudrate);
if (hb == 0) {
ESP_LOGE(TAG, "invalid baudrate: %u", baudrate);
uint32_t hb_x100 = CONFIG_DALI_CUSTOM_HALF_BIT_TIME_X100_US;
if (hb_x100 == 0) {
hb_x100 = dali_half_bit_x100_from_baud(baudrate);
}
uint32_t hb = half_bit_us_from_x100(hb_x100);
uint32_t timer_alarm_ticks = timer_alarm_ticks_from_x100(hb_x100);
if (hb == 0 || timer_alarm_ticks == 0) {
ESP_LOGE(TAG, "invalid native timing: baudrate=%u hbX100Us=%u timerHz=%u",
baudrate, hb_x100, CONFIG_DALI_TIMER_RESOLUTION_HZ);
return ESP_ERR_INVALID_ARG;
}
dali_timing_t new_timing = {
.hb = hb,
.timer_alarm_ticks = timer_alarm_ticks,
.rx_hb_min = scale_time_by_hb(DALI_RX_HB_MIN, hb),
.rx_hb_max = scale_time_by_hb(DALI_RX_HB_MAX, hb),
.rx_2hb_min = scale_time_by_hb(DALI_RX_2HB_MIN, hb),
.rx_2hb_max = scale_time_by_hb(DALI_RX_2HB_MAX, hb),
.rx_stop_cond = scale_time_by_hb(DALI_RX_STOP_COND, hb),
.rx_stop_cond = configured_or_scaled_time(CONFIG_DALI_RX_STOP_CONDITION_US,
DALI_RX_STOP_COND, hb),
.time_bus_down = scale_time_by_hb(DALI_TIME_BUS_DOWN, hb),
.time_break_min = scale_time_by_hb(DALI_TIME_BREAK_MIN, hb),
.time_recovery_min = scale_time_by_hb(DALI_TIME_RECOVERY_MIN, hb),
.tx_stop_cond = scale_time_by_hb(DALI_TX_STOP_COND, hb),
.tx_stop_cond = configured_or_scaled_time(CONFIG_DALI_TX_STOP_CONDITION_US,
DALI_TX_STOP_COND, hb),
.collision_txrx_delta = scale_time_by_hb(DALI_COLLISION_TXRX_DELTA, hb),
};
@@ -199,7 +273,7 @@ static esp_err_t apply_timer_alarm_locked(void)
return ESP_OK;
}
gptimer_alarm_config_t timer_alarm_config = {
.alarm_count = s_timing.hb,
.alarm_count = s_timing.timer_alarm_ticks,
.reload_count = 0,
.flags = {
.auto_reload_on_alarm = true,
@@ -235,22 +309,221 @@ static bool s_debug_task_created = false;
#endif
static bool s_dali_task_created = false;
#define DALI_SET_BUS_HIGH(bus) gpio_set_level((bus)->tx_pin, DALI_TX_HIGH) // set bus level
#define DALI_SET_BUS_LOW(bus) gpio_set_level((bus)->tx_pin, DALI_TX_LOW) // set bus level
#define DALI_SET_BUS_LEVEL(bus, x) gpio_set_level((bus)->tx_pin, ((x)==DALI_TX_HIGH)) // set bus level
#define DALI_SET_BUS_HIGH(bus) do { gpio_set_level((bus)->tx_pin, DALI_TX_HIGH); (bus)->tx_level = 1; } while (0)
#define DALI_SET_BUS_LOW(bus) do { gpio_set_level((bus)->tx_pin, DALI_TX_LOW); (bus)->tx_level = 0; } while (0)
#define DALI_SET_BUS_LEVEL(bus, x) do { \
uint8_t level = (x) ? 1 : 0; \
gpio_set_level((bus)->tx_pin, level ? DALI_TX_HIGH : DALI_TX_LOW); \
(bus)->tx_level = level; \
} while (0)
// !!! read from RX pin, we need real bus level, not logic level of TX pin
// return: 0 - bus level low, active state
// 1 - bus level high, idle state
#define DALI_GET_BUS_LEVEL(bus) (gpio_get_level((bus)->rx_pin) == (DALI_RX_HIGH)) // get bus level
// return: 0 - tx pin drive bus low, active state
// 1 - tx pin drive bus high, idle state
#define DALI_GET_TX_LEVEL(bus) (gpio_get_level((bus)->tx_pin) == (DALI_TX_HIGH)) // get TX pin level
// return: 0 - TX drives bus low, active state
// 1 - TX releases/drives bus high, idle state
#define DALI_GET_TX_LEVEL(bus) ((bus)->tx_level)
static inline bool bus_valid(uint8_t bus_id) {
return bus_id < DALI_PHY_COUNT && s_bus[bus_id].inited;
}
static const char *bus_state_name(dali_bus_state_t state)
{
switch (state) {
case DALI_BUS_UNKNOWN: return "unknown";
case DALI_BUS_POWER_DOWN: return "power_down";
case DALI_BUS_ERROR: return "error";
case DALI_BUS_READY: return "ready";
case DALI_BUS_TRANSMITTING: return "transmitting";
case DALI_BUS_RECEIVING: return "receiving";
case DALI_BUS_TIME_BREAK: return "time_break";
case DALI_BUS_RECOVERY: return "recovery";
default: return "invalid";
}
}
static const char *tx_state_name(dali_tx_state_t state)
{
switch (state) {
case TX_STATE_ERROR: return "error";
case TX_STATE_COLLISION: return "collision";
case TX_STATE_IDLE: return "idle";
case TX_STATE_START: return "start";
case TX_STATE_DATA: return "data";
case TX_STATE_STOP: return "stop";
default: return "invalid";
}
}
static const char *rx_state_name(dali_rx_state_t state)
{
switch (state) {
case RX_STATE_ERROR: return "error";
case RX_STATE_IDLE: return "idle";
case RX_STATE_START: return "start";
case RX_STATE_DATA: return "data";
case RX_STATE_STOP: return "stop";
case RX_STATE_END: return "end";
default: return "invalid";
}
}
static uint32_t tx_completion_timeout_ms(const Dali_msg_t *msg)
{
uint32_t hb = s_timing_ready ? s_timing.hb : DALI_TIME_HB;
uint32_t stop_us = s_timing_ready ? s_timing.tx_stop_cond : DALI_TX_STOP_COND;
uint32_t bits = msg ? msg->length : DALI_MAX_BITS;
if (bits == 0 || bits > DALI_MAX_BITS) {
bits = DALI_MAX_BITS;
}
uint64_t frame_us = ((uint64_t)(2U + bits * 2U) * hb) + stop_us;
uint32_t frame_ms = (uint32_t)((frame_us + 999U) / 1000U);
uint32_t timeout_ms = frame_ms + 30U;
if (timeout_ms < 30U) {
timeout_ms = 30U;
}
if (timeout_ms > 500U) {
timeout_ms = 500U;
}
return timeout_ms;
}
static UBaseType_t queue_waiting(QueueHandle_t queue)
{
return queue ? uxQueueMessagesWaiting(queue) : 0;
}
static void log_tx_message(const char *prefix, const dali_bus_ctx_t *bus,
const Dali_msg_t *msg, uint32_t timeout_ms)
{
if (bus == NULL || msg == NULL) {
return;
}
ESP_LOGW(TAG,
"%s bus=%u timeoutMs=%lu busState=%s txState=%s rxState=%s "
"txQ=%u replyQ=%u status=%u len=%u data=%02x %02x %02x %02x",
prefix, bus->bus_id, (unsigned long)timeout_ms,
bus_state_name(bus->bus_state), tx_state_name(bus->tx_state),
rx_state_name(bus->rx_state), (unsigned)queue_waiting(bus->tx_queue),
(unsigned)queue_waiting(bus->tx_reply_queue), msg->status, msg->length,
msg->data[0], msg->data[1], msg->data[2], msg->data[3]);
}
static void drain_tx_reply_queue(dali_bus_ctx_t *bus)
{
if (bus == NULL || bus->tx_reply_queue == NULL) {
return;
}
Dali_msg_t stale = {0};
UBaseType_t drained = 0;
while (xQueueReceive(bus->tx_reply_queue, &stale, 0) == pdTRUE) {
drained++;
}
if (drained > 0) {
ESP_LOGW(TAG,
"bus=%u dropped %u stale TX completion(s) status=%u len=%u data=%02x %02x %02x %02x",
bus->bus_id, (unsigned)drained, stale.status, stale.length, stale.data[0],
stale.data[1], stale.data[2], stale.data[3]);
}
}
static void recover_tx_timeout(dali_bus_ctx_t *bus)
{
if (bus == NULL) {
return;
}
if (bus->tx_queue) {
xQueueReset(bus->tx_queue);
}
if (bus->tx_reply_queue) {
xQueueReset(bus->tx_reply_queue);
}
bus->tx_data.status = DALI_FRAME_ERROR;
bus->tx_state = TX_STATE_IDLE;
bus->rx_state = RX_STATE_IDLE;
bus->tx_half_bit_counter = 0;
bus->tx_data_bit_counter = 0;
bus->rx_half_bit_counter = 0;
bus->rx_data_bit_counter = 0;
if (bus->bus_state == DALI_BUS_TRANSMITTING || bus->bus_state == DALI_BUS_TIME_BREAK ||
bus->bus_state == DALI_BUS_RECOVERY) {
bus->tx_last_edge_time = esp_timer_get_time();
DALI_SET_BUS_HIGH(bus);
bus->bus_state = DALI_BUS_READY;
}
ESP_LOGW(TAG, "bus=%u native TX queue recovered busState=%s txState=%s rxState=%s",
bus->bus_id, bus_state_name(bus->bus_state), tx_state_name(bus->tx_state),
rx_state_name(bus->rx_state));
}
static inline bool IRAM_ATTR interval_elapsed_us(uint64_t time_now, uint64_t last_time,
uint64_t interval_us)
{
return interval_us > 0 && (time_now - last_time) >= interval_us;
}
static void IRAM_ATTR publish_bus_abnormal_from_isr(dali_bus_ctx_t *bus, BaseType_t *yield)
{
if (bus == NULL || dali_raw_receive_queue == NULL || DALI_BUS_ABNORMAL_REPORT_INTERVAL_US == 0) {
return;
}
Dali_msg_t abnormal = {0};
abnormal.id = bus->bus_id;
abnormal.type = DALI_MSG_FORWARD;
abnormal.status = DALI_FRAME_OK;
abnormal.length = 16;
abnormal.data[0] = 0xFF;
abnormal.data[1] = 0xFD;
if (xQueueSendToBackFromISR(dali_raw_receive_queue, &abnormal, yield) != pdTRUE) {
Dali_msg_t dropped = {0};
xQueueReceiveFromISR(dali_raw_receive_queue, &dropped, yield);
xQueueSendToBackFromISR(dali_raw_receive_queue, &abnormal, yield);
}
}
static void IRAM_ATTR maybe_report_bus_abnormal_from_isr(dali_bus_ctx_t *bus, uint64_t time_now,
BaseType_t *yield)
{
if (bus == NULL || DALI_BUS_ABNORMAL_REPORT_INTERVAL_US == 0) {
return;
}
if (!interval_elapsed_us(time_now, bus->bus_abnormal_report_time,
DALI_BUS_ABNORMAL_REPORT_INTERVAL_US)) {
return;
}
publish_bus_abnormal_from_isr(bus, yield);
bus->bus_abnormal_report_time = time_now;
}
static void IRAM_ATTR poll_power_down_bus_from_isr(dali_bus_ctx_t *bus, uint64_t time_now,
BaseType_t *yield)
{
if (bus == NULL || bus->bus_state != DALI_BUS_POWER_DOWN) {
return;
}
if (interval_elapsed_us(time_now, bus->bus_level_check_time,
DALI_BUS_POWER_CHECK_INTERVAL_US)) {
const uint8_t current_level = DALI_GET_BUS_LEVEL(bus);
bus->bus_level_check_time = time_now;
if (current_level != bus->rx_level) {
bus->rx_level = current_level;
bus->rx_last_edge_time = time_now;
}
}
maybe_report_bus_abnormal_from_isr(bus, time_now, yield);
}
static inline void publish_rx_frame_from_isr(Dali_msg_t *msg, QueueHandle_t queue, BaseType_t *yield)
{
if (msg == NULL) {
@@ -264,6 +537,38 @@ static inline void publish_rx_frame_from_isr(Dali_msg_t *msg, QueueHandle_t queu
}
}
static void IRAM_ATTR complete_tx_from_isr(dali_bus_ctx_t *bus, BaseType_t *yield)
{
if (bus == NULL) {
return;
}
bus->tx_data.status = DALI_FRAME_OK;
if (bus->tx_reply_queue) {
if (xQueueSendToBackFromISR(bus->tx_reply_queue, &bus->tx_data, yield) != pdTRUE) {
Dali_msg_t dropped = {0};
xQueueReceiveFromISR(bus->tx_reply_queue, &dropped, yield);
xQueueSendToBackFromISR(bus->tx_reply_queue, &bus->tx_data, yield);
}
}
bus->tx_state = TX_STATE_IDLE;
}
static void IRAM_ATTR start_tx_collision_recovery_from_isr(dali_bus_ctx_t *bus,
uint64_t time_now)
{
if (bus == NULL) {
return;
}
bus->rx_last_edge_time = time_now;
bus->tx_last_edge_time = time_now;
DALI_SET_BUS_LOW(bus);
bus->bus_state = DALI_BUS_TIME_BREAK;
bus->tx_data.status = DALI_FRAME_COLLISION;
}
// GPIO ISR handler
// define rx_gpio_isr_handler on any edge
static void IRAM_ATTR rx_gpio_isr_handler(void* arg)
@@ -273,6 +578,7 @@ static void IRAM_ATTR rx_gpio_isr_handler(void* arg)
return;
}
BaseType_t yield = false;
uint64_t rx_current_edge_time = esp_timer_get_time(); // get time in us
uint8_t rx_previous_level = bus->rx_level;
@@ -287,8 +593,16 @@ static void IRAM_ATTR rx_gpio_isr_handler(void* arg)
// always save time of last edge
bus->rx_last_edge_time = rx_current_edge_time; // get time in us
if(bus->bus_state == DALI_BUS_READY && bus->rx_level == 0) // found start bit
bool tx_stop_released = bus->bus_state == DALI_BUS_TRANSMITTING &&
bus->tx_state == TX_STATE_STOP &&
DALI_GET_TX_LEVEL(bus) == 1;
if((bus->bus_state == DALI_BUS_READY || tx_stop_released) && bus->rx_level == 0) // found start bit
{
if (tx_stop_released) {
complete_tx_from_isr(bus, &yield);
}
// within range for backward frame
uint32_t time_ms = bus->rx_pulse_width / 1000; // 1ms = 1000us
if(time_ms>255) bus->rx_data.type = 255;
@@ -378,12 +692,13 @@ static void IRAM_ATTR rx_gpio_isr_handler(void* arg)
// if collision detected: we are too late after bit was transmitted
else if (bus->bus_state == DALI_BUS_TRANSMITTING && bus->rx_tx_delta > s_timing.collision_txrx_delta)
else if (bus->bus_state == DALI_BUS_TRANSMITTING && bus->tx_state != TX_STATE_STOP &&
bus->rx_tx_delta > s_timing.collision_txrx_delta)
{
// we need now to start collision recovery with time break: 101.9.2.4
bus->tx_last_edge_time = esp_timer_get_time(); // get time in us
DALI_SET_BUS_LOW(bus); // force TX low - active state, inform about collision, this also generate new GPIO ISR
bus->tx_last_edge_time = esp_timer_get_time(); // get time in us
bus->bus_state = DALI_BUS_TIME_BREAK; // we are in time break state
bus->tx_data.status = DALI_FRAME_COLLISION; // collision detected
}
@@ -399,7 +714,11 @@ static void IRAM_ATTR rx_gpio_isr_handler(void* arg)
dbg.rx_pulse_width = bus->rx_pulse_width;
dbg.rx_tx_delta = bus->rx_tx_delta;
dbg.bus_id = bus->bus_id;
xQueueSendToBackFromISR(rx_dbg_queue, &dbg, NULL); // send data to queue
xQueueSendToBackFromISR(rx_dbg_queue, &dbg, &yield); // send data to queue
}
if (yield) {
portYIELD_FROM_ISR();
}
}
@@ -423,6 +742,18 @@ static bool IRAM_ATTR handle_bus_timer(dali_bus_ctx_t *bus, uint64_t time_now)
bus->tx_last_edge_time = time_now - MAX_DELTA_RELOAD_TIME/2; // half of max time
}
poll_power_down_bus_from_isr(bus, time_now, &yield);
rx_delta = time_now - bus->rx_last_edge_time;
tx_delta = time_now - bus->tx_last_edge_time;
if (bus->bus_state == DALI_BUS_TRANSMITTING && bus->tx_state == TX_STATE_STOP &&
DALI_GET_TX_LEVEL(bus) == 1 &&
(bus->rx_state == RX_STATE_START || bus->rx_state == RX_STATE_DATA ||
bus->rx_state == RX_STATE_STOP)) {
complete_tx_from_isr(bus, &yield);
bus->bus_state = DALI_BUS_RECEIVING;
}
// recovery from different error states: UNKNOWN, ERROR, POWER_DOWN
if(bus->bus_state <= DALI_BUS_ERROR)
{ // 101.8.2.4 - startup BUS after 2.4ms
@@ -434,8 +765,13 @@ static bool IRAM_ATTR handle_bus_timer(dali_bus_ctx_t *bus, uint64_t time_now)
// if bus power down - if bus is low for more then 45ms
if(bus->rx_level==0 && rx_delta > s_timing.time_bus_down)
{ // power lost
bool entered_power_down = bus->bus_state != DALI_BUS_POWER_DOWN;
bus->bus_state = DALI_BUS_POWER_DOWN; // bus is power down - recovery see previous if
DALI_SET_BUS_HIGH(bus); // make sure TX is high
if (entered_power_down) {
bus->bus_level_check_time = time_now;
bus->bus_abnormal_report_time = time_now;
}
}
// recovery from collision detection
@@ -444,9 +780,9 @@ static bool IRAM_ATTR handle_bus_timer(dali_bus_ctx_t *bus, uint64_t time_now)
// BUS: ACTIVE, LOW - BUS is busy, let caller to restart transmission
if(bus->bus_state == DALI_BUS_TIME_BREAK && rx_delta > s_timing.time_break_min)
{
bus->tx_last_edge_time = esp_timer_get_time(); // get time in us
DALI_SET_BUS_HIGH(bus); // TX high - idle state - generate ISR on RX pin
bus->tx_last_edge_time = esp_timer_get_time(); // get time in us
// read bus state
if(DALI_GET_BUS_LEVEL(bus) == 0) // other device is keeping bus low
{
@@ -474,26 +810,31 @@ static bool IRAM_ATTR handle_bus_timer(dali_bus_ctx_t *bus, uint64_t time_now)
bus->tx_state = TX_STATE_START; // start transmitting
bus->tx_half_bit_counter = 0;
bus->tx_data_bit_counter = 0; // actually sent bits count
DALI_SET_BUS_LOW(bus); // start bit first half
bus->tx_last_edge_time = esp_timer_get_time(); // get time in us
DALI_SET_BUS_LOW(bus); // start bit first half
}
else if(bus->bus_state == DALI_BUS_TRANSMITTING)
{
uint8_t bus_level = DALI_GET_BUS_LEVEL(bus);
if(bus->tx_state != TX_STATE_STOP && tx_delta > s_timing.collision_txrx_delta &&
bus_level != DALI_GET_TX_LEVEL(bus)) {
bus->rx_level = bus_level;
bus->rx_tx_delta = tx_delta;
start_tx_collision_recovery_from_isr(bus, time_now);
}
// transmit data
if(bus->tx_state == TX_STATE_START) {
else if(bus->tx_state == TX_STATE_START) {
bus->tx_state = TX_STATE_DATA; // start transmitting data
bus->tx_half_bit_counter++;
DALI_SET_BUS_HIGH(bus); // start bit second half
bus->tx_last_edge_time = esp_timer_get_time(); // get time in us
DALI_SET_BUS_HIGH(bus); // start bit second half
}
else if(bus->tx_state == TX_STATE_DATA) {
bool value = (bus->tx_data.data[bus->tx_data_bit_counter/8] >> ( 7 - (bus->tx_data_bit_counter % 8) )) & 0x01;
value ^= bus->tx_half_bit_counter & 0x01; // xor=invert value for odd half bit 1:0->1 and 0:1->0
bus->tx_last_edge_time = esp_timer_get_time(); // get time in us
DALI_SET_BUS_LEVEL(bus, value);
bus->tx_last_edge_time = esp_timer_get_time(); // get time in us
bus->tx_half_bit_counter++; // increment half bit counter before next test
if(bus->tx_half_bit_counter & 0x01) { // next bit
bus->tx_data_bit_counter++;
@@ -506,15 +847,11 @@ static bool IRAM_ATTR handle_bus_timer(dali_bus_ctx_t *bus, uint64_t time_now)
// here we check TX (NOT RX) bit state
if(DALI_GET_TX_LEVEL(bus) == 0) // really ok - otherwise we will keep bus low forever
{
DALI_SET_BUS_HIGH(bus);
bus->tx_last_edge_time = esp_timer_get_time(); // get time in us
DALI_SET_BUS_HIGH(bus);
}
else if(tx_delta > s_timing.tx_stop_cond) {
bus->tx_data.status = DALI_FRAME_OK; // frame is OK
if (bus->tx_reply_queue) {
xQueueSendToBackFromISR(bus->tx_reply_queue, &bus->tx_data, &yield); // send data to queue
}
bus->tx_state = TX_STATE_IDLE; // final state with transmitted data
complete_tx_from_isr(bus, &yield);
bus->bus_state = DALI_BUS_READY; // bus is ready
}
}
@@ -552,6 +889,10 @@ static bool IRAM_ATTR handle_bus_timer(dali_bus_ctx_t *bus, uint64_t time_now)
publish_rx_frame_from_isr(&bus->rx_data, bus->rx_queue, &yield); // send data to queue
}
}
else if(bus->rx_state == RX_STATE_START && rx_delta > s_timing.rx_hb_max) {
bus->rx_state = RX_STATE_ERROR;
bus->rx_data.status = DALI_FRAME_TIME_VIOLATION;
}
}
return yield;
}
@@ -588,7 +929,7 @@ static void debug_task(void *pvParameters)
else v = '0' + dbg.level;
}
else v=' ';
printf("bus[%u] rx: [%2d] pw=%lu v=%u rtd=%lu [%c]",
ESP_LOGD(TAG, "bus[%u] rx: [%2d] pw=%lu v=%u rtd=%lu [%c]",
dbg.bus_id, i++, dbg.rx_pulse_width, dbg.level, dbg.rx_tx_delta, v);
if(dbg.rx_pulse_width < 1000) {
if(dbg.rx_pulse_width > 550) HB+=2;
@@ -659,7 +1000,7 @@ static esp_err_t ensure_timer_started(void)
.flags = {
.intr_shared = true,
},
.resolution_hz = 1 * 1000 * 1000, // 1MHz, 1 tick = 1us
.resolution_hz = CONFIG_DALI_TIMER_RESOLUTION_HZ,
};
err = gptimer_new_timer(&timer_config, &gptimer);
if (err != ESP_OK) {
@@ -726,6 +1067,9 @@ static esp_err_t init_bus(uint8_t bus_id, uint8_t tx_pin, uint8_t rx_pin)
bus->bus_id = bus_id;
bus->tx_pin = tx_pin;
bus->rx_pin = rx_pin;
bus->tx_level = 1;
gpio_set_level(bus->tx_pin, DALI_TX_HIGH);
gpio_config_t io_conf;
@@ -771,6 +1115,8 @@ static esp_err_t init_bus(uint8_t bus_id, uint8_t tx_pin, uint8_t rx_pin)
bus->rx_last_edge_time = esp_timer_get_time(); // get time in us - startup time
bus->rx_level = DALI_GET_BUS_LEVEL(bus); // get level of RX pin
bus->tx_last_edge_time = bus->rx_last_edge_time;
bus->bus_level_check_time = bus->rx_last_edge_time;
bus->bus_abnormal_report_time = 0;
err = ensure_isr_service();
if (err != ESP_OK) {
@@ -805,17 +1151,45 @@ static int dali_tx_bus(dali_bus_ctx_t *bus, Dali_msg_t *dali_msg)
if (bus == NULL || !bus->inited) {
return ESP_FAIL;
}
if(xQueueSendToBack(bus->tx_queue, dali_msg, pdMS_TO_TICKS(50)) == pdFALSE) {
xQueueReset(bus->tx_queue); // clear queue
printf("dali_tx: Queue full\n");
if (bus->tx_queue == NULL || bus->tx_reply_queue == NULL || dali_msg == NULL) {
return ESP_FAIL;
}
if(xQueueReceive(bus->tx_reply_queue, dali_msg, pdMS_TO_TICKS(50)) == pdFALSE) {
xQueueReset(bus->tx_reply_queue); // clear queue
printf("dali_tx: No reply\n");
const uint32_t timeout_ms = tx_completion_timeout_ms(dali_msg);
const TickType_t timeout_ticks = pdMS_TO_TICKS(timeout_ms);
drain_tx_reply_queue(bus);
if (bus->bus_state == DALI_BUS_POWER_DOWN) {
dali_msg->status = DALI_FRAME_ERROR;
log_tx_message("native TX skipped, bus power down", bus, dali_msg, timeout_ms);
return ESP_FAIL;
}
return ESP_OK;
ESP_LOGD(TAG, "bus=%u queue TX len=%u data=%02x %02x %02x %02x timeoutMs=%lu",
bus->bus_id, dali_msg->length, dali_msg->data[0], dali_msg->data[1],
dali_msg->data[2], dali_msg->data[3], (unsigned long)timeout_ms);
if(xQueueSendToBack(bus->tx_queue, dali_msg, timeout_ticks) == pdFALSE) {
log_tx_message("native TX queue full", bus, dali_msg, timeout_ms);
if (bus->tx_state == TX_STATE_IDLE) {
xQueueReset(bus->tx_queue);
}
return ESP_FAIL;
}
if(xQueueReceive(bus->tx_reply_queue, dali_msg, timeout_ticks) == pdFALSE) {
if (bus->tx_state == TX_STATE_IDLE && queue_waiting(bus->tx_queue) == 0) {
*dali_msg = bus->tx_data;
log_tx_message("native TX completion queue missed", bus, dali_msg, timeout_ms);
return dali_msg->status == DALI_FRAME_OK ? ESP_OK : ESP_FAIL;
}
log_tx_message("native TX completion timeout", bus, dali_msg, timeout_ms);
recover_tx_timeout(bus);
return ESP_FAIL;
}
ESP_LOGD(TAG, "bus=%u TX complete status=%u len=%u data=%02x %02x %02x %02x",
bus->bus_id, dali_msg->status, dali_msg->length, dali_msg->data[0],
dali_msg->data[1], dali_msg->data[2], dali_msg->data[3]);
return dali_msg->status == DALI_FRAME_OK ? ESP_OK : ESP_FAIL;
}
// dali_task - should run at highest priority
@@ -843,6 +1217,7 @@ void dali_task(void *pvParameters)
esp_err_t dali_hal_init(uint8_t dali_id, uint8_t tx_pin, uint8_t rx_pin)
{
dali_hal_lock();
apply_dali_log_level();
esp_err_t err = ensure_timing_ready_locked();
if (err == ESP_OK) {
@@ -873,6 +1248,7 @@ esp_err_t dali_hal_init(uint8_t dali_id, uint8_t tx_pin, uint8_t rx_pin)
esp_err_t dali_hal_set_baudrate(uint32_t baudrate)
{
dali_hal_lock();
apply_dali_log_level();
bool resume_timer = s_timer_started && gptimer;
if (resume_timer) {
+8
View File
@@ -3,6 +3,10 @@
#include <stdint.h>
#include "dali_hal.h"
#ifdef __cplusplus
extern "C" {
#endif
/*
Addressing: 102.7.2.1
0AAA AAAx - short address AAAAAA 0-63
@@ -77,5 +81,9 @@ void dali_set_search_addr24(uint32_t addr24);
uint32_t dali_binary_search();
void dali_change_short_address(int addr1, int addr2);
#ifdef __cplusplus
}
#endif
+24 -7
View File
@@ -8,6 +8,10 @@
#include "freertos/task.h"
#include "freertos/queue.h"
#ifdef __cplusplus
extern "C" {
#endif
#ifdef CONFIG_DALI_PHY_COUNT
#define DALI_PHY_COUNT CONFIG_DALI_PHY_COUNT
#else
@@ -182,13 +186,22 @@ typedef struct Dali_msg Dali_msg_t;
// define HW DALI for gpio functions
#ifndef DALI_HW_PINS
// define HW DALI for gpio functions
// - what we should write to pin to get HIGH/LOW
#define DALI_TX_HIGH 1 // idle state
#define DALI_TX_LOW 0 // active state
// - what we should read from pin to get HIGH/LOW
#define DALI_RX_HIGH 1 // idle state
#define DALI_RX_LOW 0 // active state
// Physical GPIO levels for logical DALI idle/high and active/low bus states.
#ifdef CONFIG_DALI_TX_ACTIVE_HIGH
#define DALI_TX_HIGH 0 // idle state
#define DALI_TX_LOW 1 // active state
#else
#define DALI_TX_HIGH 1 // idle state
#define DALI_TX_LOW 0 // active state
#endif
#ifdef CONFIG_DALI_RX_ACTIVE_LOW
#define DALI_RX_HIGH 1 // idle state
#define DALI_RX_LOW 0 // active state
#else
#define DALI_RX_HIGH 0 // idle state
#define DALI_RX_LOW 1 // active state
#endif
#endif
// LED onboard - debug
@@ -228,3 +241,7 @@ esp_err_t dali_hal_get_bus_info(uint8_t bus_id, dali_hal_bus_info_t *info);
QueueHandle_t dali_hal_raw_receive_queue(void);
void dali_task(void *pvParameters);
#ifdef __cplusplus
}
#endif
+22 -6
View File
@@ -64,6 +64,10 @@ struct SerialRxPacket {
uint8_t data[kSerialRxPacketMaxBytes]{};
};
std::vector<uint8_t> LegacyQueryResponse(uint8_t status, uint8_t value = 0x00) {
return {status, value};
}
bool SendHardwareFrame(uint8_t bus_id, const uint8_t* data, size_t len) {
if (data == nullptr || len != 3) {
return false;
@@ -89,9 +93,12 @@ bool SendHardwareFrame(uint8_t bus_id, const uint8_t* data, size_t len) {
}
std::vector<uint8_t> TransactHardwareFrame(uint8_t bus_id, const uint8_t* data, size_t len) {
if (data == nullptr || len != 3) {
if (data == nullptr) {
return {};
}
if (len != 3) {
return len > 0 && data[0] == 0x12 ? LegacyQueryResponse(0xFD) : std::vector<uint8_t>{};
}
switch (data[0]) {
case 0x00:
@@ -107,9 +114,12 @@ std::vector<uint8_t> TransactHardwareFrame(uint8_t bus_id, const uint8_t* data,
tx.id = bus_id;
Dali_msg_t rx = {};
if (dali_query(&tx, &rx) == pdTRUE) {
if (rx.status != DALI_FRAME_OK || rx.length != 8) {
return LegacyQueryResponse(0xFD);
}
return {0xFF, rx.data[0]};
}
return {0xFE};
return LegacyQueryResponse(0xFE);
}
default:
return {};
@@ -151,13 +161,19 @@ std::vector<uint8_t> ReadSerialFrame(QueueHandle_t queue, size_t len, uint32_t t
std::vector<uint8_t> TransactSerialFrame(int uart_port, QueueHandle_t queue,
uint32_t query_timeout_ms, const uint8_t* data,
size_t len) {
if (data == nullptr || len == 0) {
return LegacyQueryResponse(0xFD);
}
if (data[0] == 0x12 && len != 3) {
return LegacyQueryResponse(0xFD);
}
if (data != nullptr && len > 0 && data[0] == 0x12) {
DrainSerialQueue(queue);
}
if (!WriteSerialFrame(uart_port, data, len)) {
return {0xFD};
return LegacyQueryResponse(0xFD);
}
if (data == nullptr || len == 0 || data[0] != 0x12) {
if (data[0] != 0x12) {
return {0xFF};
}
@@ -173,10 +189,10 @@ std::vector<uint8_t> TransactSerialFrame(int uart_port, QueueHandle_t queue,
auto response = PacketToVector(packet, 2);
if (!response.empty() &&
(response[0] == 0xFF || response[0] == 0xFE || response[0] == 0xFD)) {
return response;
return LegacyQueryResponse(response[0], response.size() > 1 ? response[1] : 0x00);
}
}
return {0xFE};
return LegacyQueryResponse(0xFE);
}
} // namespace
@@ -2062,12 +2062,14 @@ struct GatewayBridgeService::ChannelRuntime {
const bool commissioning_only = !knx_config.has_value();
ESP_LOGI(kTag,
"gateway=%u KNX/IP start config namespace=%s storedConfig=%d udp=%u tunnel=%d "
"multicast=%d multicastGroup=%s mainGroup=%u tpUart=%d tx=%d rx=%d individual=0x%04x",
"multicast=%d multicastGroup=%s mainGroup=%u tpUart=%d tx=%d rx=%d nineBit=%d "
"individual=0x%04x",
channel.gateway_id, openKnxNamespace().c_str(), !commissioning_only,
static_cast<unsigned>(runtime_config.udp_port), runtime_config.tunnel_enabled,
runtime_config.multicast_enabled, runtime_config.multicast_address.c_str(),
static_cast<unsigned>(runtime_config.main_group), runtime_config.tp_uart.uart_port,
runtime_config.tp_uart.tx_pin, runtime_config.tp_uart.rx_pin,
runtime_config.tp_uart.nine_bit_mode,
runtime_config.individual_address);
knx->setConfig(runtime_config);
knx_router->setConfig(runtime_config);
@@ -2304,6 +2306,7 @@ struct GatewayBridgeService::ChannelRuntime {
cJSON_AddNumberToObject(serial_json, "txPin", effective_knx->tp_uart.tx_pin);
cJSON_AddNumberToObject(serial_json, "rxPin", effective_knx->tp_uart.rx_pin);
cJSON_AddNumberToObject(serial_json, "baudrate", effective_knx->tp_uart.baudrate);
cJSON_AddBoolToObject(serial_json, "nineBitMode", effective_knx->tp_uart.nine_bit_mode);
cJSON_AddItemToObject(knx_json, "tpUart", serial_json);
}
}
@@ -39,6 +39,7 @@ struct GatewayKnxTpUartConfig {
size_t rx_buffer_size{1024};
size_t tx_buffer_size{1024};
uint32_t read_timeout_ms{20};
bool nine_bit_mode{true};
};
enum class GatewayKnxMappingMode : uint8_t {
@@ -348,6 +349,8 @@ class GatewayKnxTpIpRouter {
int tcp_sock_{-1};
int active_tcp_sock_{-1};
int tp_uart_port_{-1};
int tp_uart_tx_pin_{-1};
int tp_uart_rx_pin_{-1};
std::vector<uint32_t> multicast_joined_interfaces_;
TickType_t network_refresh_tick_{0};
std::array<TcpClient, kMaxTcpClients> tcp_clients_{};
+76 -12
View File
@@ -9,6 +9,7 @@
#include "lwip/inet.h"
#include "lwip/sockets.h"
#include "openknx_idf/ets_device_runtime.h"
#include "soc/uart_periph.h"
#include <algorithm>
#include <array>
@@ -173,6 +174,38 @@ std::string ErrnoDetail(const std::string& message, int err) {
return std::string(message) + ": errno=" + std::to_string(err) + " (" + std::strerror(err) + ")";
}
bool ResolveUartIoPin(uart_port_t uart_port, int configured_pin, uint32_t pin_index,
int* resolved_pin) {
if (resolved_pin == nullptr) {
return false;
}
if (configured_pin >= 0) {
*resolved_pin = configured_pin;
return true;
}
if (uart_port < 0 || uart_port >= SOC_UART_NUM || pin_index >= SOC_UART_PINS_COUNT) {
*resolved_pin = UART_PIN_NO_CHANGE;
return false;
}
const int default_pin = uart_periph_signal[uart_port].pins[pin_index].default_gpio;
if (default_pin < 0) {
*resolved_pin = UART_PIN_NO_CHANGE;
return false;
}
*resolved_pin = default_pin;
return true;
}
std::string UartPinDescription(int configured_pin, int resolved_pin) {
if (configured_pin >= 0) {
return std::to_string(configured_pin);
}
if (resolved_pin >= 0) {
return std::to_string(resolved_pin) + " (default from -1)";
}
return "unrouted (-1 with no target default)";
}
std::string Ipv4String(uint32_t network_address) {
const uint32_t address = ntohl(network_address);
char buffer[16]{};
@@ -909,6 +942,9 @@ std::optional<GatewayKnxConfig> GatewayKnxConfigFromValue(const DaliValue* value
config.tp_uart.read_timeout_ms = static_cast<uint32_t>(std::max(
1, ObjectIntAny(serial, {"readTimeoutMs", "read_timeout_ms"})
.value_or(static_cast<int>(config.tp_uart.read_timeout_ms))));
config.tp_uart.nine_bit_mode = ObjectBoolAny(
serial, {"nineBitMode", "nine_bit_mode", "use9BitMode", "use_9_bit_mode"})
.value_or(config.tp_uart.nine_bit_mode);
}
return config;
}
@@ -939,6 +975,7 @@ DaliValue GatewayKnxConfigToValue(const GatewayKnxConfig& config) {
serial["rxBufferSize"] = static_cast<int>(config.tp_uart.rx_buffer_size);
serial["txBufferSize"] = static_cast<int>(config.tp_uart.tx_buffer_size);
serial["readTimeoutMs"] = static_cast<int>(config.tp_uart.read_timeout_ms);
serial["nineBitMode"] = config.tp_uart.nine_bit_mode;
out["tpUart"] = std::move(serial);
DaliValue::Array ets_associations;
ets_associations.reserve(config.ets_associations.size());
@@ -1911,13 +1948,24 @@ esp_err_t GatewayKnxTpIpRouter::start(uint32_t task_stack_size, UBaseType_t task
}
stop_requested_ = false;
last_error_.clear();
int log_tp_uart_tx_pin = -1;
int log_tp_uart_rx_pin = -1;
if (config_.tp_uart.uart_port >= 0 && config_.tp_uart.uart_port < SOC_UART_NUM) {
const uart_port_t log_uart_port = static_cast<uart_port_t>(config_.tp_uart.uart_port);
ResolveUartIoPin(log_uart_port, config_.tp_uart.tx_pin, SOC_UART_TX_PIN_IDX,
&log_tp_uart_tx_pin);
ResolveUartIoPin(log_uart_port, config_.tp_uart.rx_pin, SOC_UART_RX_PIN_IDX,
&log_tp_uart_rx_pin);
}
ESP_LOGI(kTag,
"starting KNXnet/IP router namespace=%s udp=%u tunnel=%d multicast=%d group=%s "
"tpUart=%d tx=%d rx=%d commissioningOnly=%d",
"tpUart=%d tx=%s rx=%s nineBit=%d commissioningOnly=%d",
openknx_namespace_.c_str(), static_cast<unsigned>(config_.udp_port),
config_.tunnel_enabled, config_.multicast_enabled,
config_.multicast_address.c_str(), config_.tp_uart.uart_port,
config_.tp_uart.tx_pin, config_.tp_uart.rx_pin, commissioning_only_);
UartPinDescription(config_.tp_uart.tx_pin, log_tp_uart_tx_pin).c_str(),
UartPinDescription(config_.tp_uart.rx_pin, log_tp_uart_rx_pin).c_str(),
config_.tp_uart.nine_bit_mode, commissioning_only_);
if (!configureSocket()) {
return ESP_FAIL;
}
@@ -2447,11 +2495,24 @@ bool GatewayKnxTpIpRouter::configureTpUart() {
uart_config_t uart_config{};
uart_config.baud_rate = static_cast<int>(serial.baudrate);
uart_config.data_bits = UART_DATA_8_BITS;
uart_config.parity = UART_PARITY_EVEN;
uart_config.parity = serial.nine_bit_mode ? UART_PARITY_EVEN : UART_PARITY_DISABLE;
uart_config.stop_bits = UART_STOP_BITS_1;
uart_config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
uart_config.source_clk = UART_SCLK_DEFAULT;
const uart_port_t uart_port = static_cast<uart_port_t>(serial.uart_port);
int tx_pin = UART_PIN_NO_CHANGE;
int rx_pin = UART_PIN_NO_CHANGE;
const bool tx_pin_ok = ResolveUartIoPin(uart_port, serial.tx_pin, SOC_UART_TX_PIN_IDX, &tx_pin);
const bool rx_pin_ok = ResolveUartIoPin(uart_port, serial.rx_pin, SOC_UART_RX_PIN_IDX, &rx_pin);
if (!tx_pin_ok || !rx_pin_ok) {
last_error_ = "KNX TP-UART UART" + std::to_string(serial.uart_port) +
" has no ESP-IDF default " + (!tx_pin_ok ? std::string("TX") : std::string("")) +
(!tx_pin_ok && !rx_pin_ok ? "/" : "") +
(!rx_pin_ok ? std::string("RX") : std::string("")) +
" pin; configure explicit txPin/rxPin values";
ESP_LOGE(kTag, "%s", last_error_.c_str());
return false;
}
esp_err_t err = uart_param_config(uart_port, &uart_config);
if (err != ESP_OK) {
last_error_ = EspErrDetail("failed to configure KNX TP-UART parameters on UART" +
@@ -2460,14 +2521,12 @@ bool GatewayKnxTpIpRouter::configureTpUart() {
ESP_LOGE(kTag, "%s", last_error_.c_str());
return false;
}
err = uart_set_pin(uart_port, serial.tx_pin < 0 ? UART_PIN_NO_CHANGE : serial.tx_pin,
serial.rx_pin < 0 ? UART_PIN_NO_CHANGE : serial.rx_pin,
UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
err = uart_set_pin(uart_port, tx_pin, rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
if (err != ESP_OK) {
last_error_ = EspErrDetail("failed to configure KNX TP-UART pins uart=" +
std::to_string(serial.uart_port) + " tx=" +
std::to_string(serial.tx_pin) + " rx=" +
std::to_string(serial.rx_pin),
UartPinDescription(serial.tx_pin, tx_pin) + " rx=" +
UartPinDescription(serial.rx_pin, rx_pin),
err);
ESP_LOGE(kTag, "%s", last_error_.c_str());
return false;
@@ -2482,6 +2541,8 @@ bool GatewayKnxTpIpRouter::configureTpUart() {
return false;
}
tp_uart_port_ = serial.uart_port;
tp_uart_tx_pin_ = tx_pin;
tp_uart_rx_pin_ = rx_pin;
if (!initializeTpUart()) {
if (ets_device_ != nullptr && !ets_device_->configured()) {
ESP_LOGW(kTag,
@@ -2496,8 +2557,11 @@ bool GatewayKnxTpIpRouter::configureTpUart() {
ESP_LOGE(kTag, "%s", last_error_.c_str());
return false;
}
ESP_LOGI(kTag, "KNX TP-UART online uart=%d tx=%d rx=%d baud=%u", serial.uart_port,
serial.tx_pin, serial.rx_pin, static_cast<unsigned>(serial.baudrate));
ESP_LOGI(kTag, "KNX TP-UART online uart=%d tx=%s rx=%s baud=%u nineBit=%d",
serial.uart_port,
UartPinDescription(serial.tx_pin, tp_uart_tx_pin_).c_str(),
UartPinDescription(serial.rx_pin, tp_uart_rx_pin_).c_str(),
static_cast<unsigned>(serial.baudrate), serial.nine_bit_mode);
return true;
}
@@ -2600,8 +2664,8 @@ bool GatewayKnxTpIpRouter::initializeTpUart() {
last_error_ = (saw_reset ? "timed out waiting for KNX TP-UART state indication"
: "timed out waiting for KNX TP-UART reset indication") +
std::string(" uart=") + std::to_string(config_.tp_uart.uart_port) +
" tx=" + std::to_string(config_.tp_uart.tx_pin) +
" rx=" + std::to_string(config_.tp_uart.rx_pin) +
" tx=" + UartPinDescription(config_.tp_uart.tx_pin, tp_uart_tx_pin_) +
" rx=" + UartPinDescription(config_.tp_uart.rx_pin, tp_uart_rx_pin_) +
" timeoutMs=1500";
return false;
}
@@ -82,6 +82,19 @@ void GatewayUsbSetupBridge::handleBytes(const uint8_t* data, size_t len) {
}
const uint8_t gateway_id = setupGatewayId();
if (data[0] == 0x12) {
const auto response = dali_domain_.transactBridgeFrame(gateway_id, data, len);
if (!response.empty()) {
const int written = usb_serial_jtag_write_bytes(response.data(), response.size(),
pdMS_TO_TICKS(config_.write_timeout_ms));
if (written < 0 || static_cast<size_t>(written) != response.size()) {
ESP_LOGW(kTag, "failed to write USB raw query response channel=%u len=%u",
config_.channel_index, static_cast<unsigned>(response.size()));
}
}
return;
}
if (!dali_domain_.writeBridgeFrame(gateway_id, data, len)) {
ESP_LOGW(kTag, "failed to write USB raw setup frame channel=%u len=%u", config_.channel_index,
static_cast<unsigned>(len));
@@ -14,7 +14,8 @@ namespace gateway::openknx {
class TpuartUartInterface : public TPUart::Interface::Abstract {
public:
TpuartUartInterface(uart_port_t uart_port, int tx_pin, int rx_pin,
size_t rx_buffer_size = 512, size_t tx_buffer_size = 512);
size_t rx_buffer_size = 512, size_t tx_buffer_size = 512,
bool nine_bit_mode = true);
~TpuartUartInterface();
void begin(int baud) override;
@@ -34,6 +35,7 @@ class TpuartUartInterface : public TPUart::Interface::Abstract {
int rx_pin_;
size_t rx_buffer_size_;
size_t tx_buffer_size_;
bool nine_bit_mode_;
std::atomic_bool overflow_{false};
std::function<bool()> callback_;
};
@@ -80,6 +80,7 @@ EtsDeviceRuntime::EtsDeviceRuntime(std::string nvs_namespace,
? tunnel_client_address
: DefaultTunnelClientAddress(
device_.deviceObject().individualAddress()));
server->deviceAddressPropertiesTargetClient(false);
server->tunnelFrameCallback(&EtsDeviceRuntime::EmitTunnelFrame, this);
}
device_.functionPropertyCallback(&EtsDeviceRuntime::HandleFunctionPropertyCommand);
@@ -1,6 +1,7 @@
#include "openknx_idf/tpuart_uart_interface.h"
#include "esp_log.h"
#include "soc/uart_periph.h"
#include <utility>
@@ -9,15 +10,39 @@ namespace {
constexpr const char* kTag = "openknx_tpuart";
bool ResolveUartIoPin(uart_port_t uart_port, int configured_pin, uint32_t pin_index,
int* resolved_pin) {
if (resolved_pin == nullptr) {
return false;
}
if (configured_pin >= 0) {
*resolved_pin = configured_pin;
return true;
}
if (uart_port < 0 || uart_port >= SOC_UART_NUM || pin_index >= SOC_UART_PINS_COUNT) {
*resolved_pin = UART_PIN_NO_CHANGE;
return false;
}
const int default_pin = uart_periph_signal[uart_port].pins[pin_index].default_gpio;
if (default_pin < 0) {
*resolved_pin = UART_PIN_NO_CHANGE;
return false;
}
*resolved_pin = default_pin;
return true;
}
} // namespace
TpuartUartInterface::TpuartUartInterface(uart_port_t uart_port, int tx_pin, int rx_pin,
size_t rx_buffer_size, size_t tx_buffer_size)
size_t rx_buffer_size, size_t tx_buffer_size,
bool nine_bit_mode)
: uart_port_(uart_port),
tx_pin_(tx_pin),
rx_pin_(rx_pin),
rx_buffer_size_(rx_buffer_size),
tx_buffer_size_(tx_buffer_size) {}
tx_buffer_size_(tx_buffer_size),
nine_bit_mode_(nine_bit_mode) {}
TpuartUartInterface::~TpuartUartInterface() { end(); }
@@ -29,22 +54,30 @@ void TpuartUartInterface::begin(int baud) {
uart_config_t config{};
config.baud_rate = baud;
config.data_bits = UART_DATA_8_BITS;
config.parity = UART_PARITY_EVEN;
config.parity = nine_bit_mode_ ? UART_PARITY_EVEN : UART_PARITY_DISABLE;
config.stop_bits = UART_STOP_BITS_1;
config.flow_ctrl = UART_HW_FLOWCTRL_DISABLE;
config.source_clk = UART_SCLK_DEFAULT;
int tx_pin = UART_PIN_NO_CHANGE;
int rx_pin = UART_PIN_NO_CHANGE;
if (!ResolveUartIoPin(uart_port_, tx_pin_, SOC_UART_TX_PIN_IDX, &tx_pin) ||
!ResolveUartIoPin(uart_port_, rx_pin_, SOC_UART_RX_PIN_IDX, &rx_pin)) {
ESP_LOGE(kTag, "UART%d has no ESP-IDF default TX/RX pin; configure explicit pins",
uart_port_);
return;
}
esp_err_t err = uart_param_config(uart_port_, &config);
if (err != ESP_OK) {
ESP_LOGE(kTag, "failed to configure UART%d: %s", uart_port_, esp_err_to_name(err));
return;
}
err = uart_set_pin(uart_port_, tx_pin_ < 0 ? UART_PIN_NO_CHANGE : tx_pin_,
rx_pin_ < 0 ? UART_PIN_NO_CHANGE : rx_pin_, UART_PIN_NO_CHANGE,
UART_PIN_NO_CHANGE);
err = uart_set_pin(uart_port_, tx_pin, rx_pin, UART_PIN_NO_CHANGE, UART_PIN_NO_CHANGE);
if (err != ESP_OK) {
ESP_LOGE(kTag, "failed to route UART%d pins: %s", uart_port_, esp_err_to_name(err));
ESP_LOGE(kTag, "failed to route UART%d pins tx=%d rx=%d: %s", uart_port_, tx_pin,
rx_pin, esp_err_to_name(err));
return;
}
+1 -1
Submodule knx updated: 5d7d6e573b...346b704cbe