Files
gateway/apps/gateway/main/app_main.cpp
T

434 lines
16 KiB
C++

#include "dali_domain.hpp"
#include "gateway_ble.hpp"
#include "gateway_controller.hpp"
#include "gateway_core.hpp"
#include "gateway_network.hpp"
#include "gateway_runtime.hpp"
#include "gateway_usb_setup.hpp"
#include "esp_log.h"
#include "sdkconfig.h"
#include <cstdio>
#include <cstdint>
#include <memory>
#ifndef CONFIG_GATEWAY_CHANNEL_COUNT
#define CONFIG_GATEWAY_CHANNEL_COUNT 2
#endif
#ifndef CONFIG_GATEWAY_NETWORK_HTTP_PORT
#define CONFIG_GATEWAY_NETWORK_HTTP_PORT 80
#endif
#ifndef CONFIG_GATEWAY_NETWORK_UDP_PORT
#define CONFIG_GATEWAY_NETWORK_UDP_PORT 2020
#endif
#ifndef CONFIG_GATEWAY_STATUS_LED_GPIO
#define CONFIG_GATEWAY_STATUS_LED_GPIO -1
#endif
#ifndef CONFIG_GATEWAY_BOOT_BUTTON_GPIO
#define CONFIG_GATEWAY_BOOT_BUTTON_GPIO -1
#endif
#ifndef CONFIG_GATEWAY_BOOT_BUTTON_LONG_PRESS_MS
#define CONFIG_GATEWAY_BOOT_BUTTON_LONG_PRESS_MS 3000
#endif
#ifndef CONFIG_GATEWAY_USB_SETUP_CHANNEL_INDEX
#define CONFIG_GATEWAY_USB_SETUP_CHANNEL_INDEX 0
#endif
#ifndef CONFIG_GATEWAY_USB_SETUP_RX_BUFFER
#define CONFIG_GATEWAY_USB_SETUP_RX_BUFFER 256
#endif
#ifndef CONFIG_GATEWAY_USB_SETUP_TX_BUFFER
#define CONFIG_GATEWAY_USB_SETUP_TX_BUFFER 256
#endif
#ifndef CONFIG_GATEWAY_USB_SETUP_READ_TIMEOUT_MS
#define CONFIG_GATEWAY_USB_SETUP_READ_TIMEOUT_MS 20
#endif
#ifndef CONFIG_GATEWAY_SMARTCONFIG_TIMEOUT_SEC
#define CONFIG_GATEWAY_SMARTCONFIG_TIMEOUT_SEC 60
#endif
namespace {
constexpr const char* kProjectName = "DALI_485_Gateway";
constexpr const char* kProjectVersion = "0.1.0";
constexpr const char* kTag = "gateway_main";
#ifdef CONFIG_GATEWAY_WIFI_SUPPORTED
constexpr bool kWifiSupported = true;
#else
constexpr bool kWifiSupported = false;
#endif
#ifdef CONFIG_GATEWAY_START_WIFI_STA_ENABLED
constexpr bool kWifiStartupEnabled = true;
#else
constexpr bool kWifiStartupEnabled = false;
#endif
#ifdef CONFIG_GATEWAY_BLE_SUPPORTED
constexpr bool kBleSupported = true;
#else
constexpr bool kBleSupported = false;
#endif
#ifdef CONFIG_GATEWAY_START_BLE_ENABLED
constexpr bool kBleStartupEnabled = true;
#else
constexpr bool kBleStartupEnabled = false;
#endif
#ifdef CONFIG_GATEWAY_ESPNOW_SETUP_SUPPORTED
constexpr bool kEspnowSetupSupported = true;
#else
constexpr bool kEspnowSetupSupported = false;
#endif
#ifdef CONFIG_GATEWAY_START_ESPNOW_SETUP_ENABLED
constexpr bool kEspnowSetupStartupEnabled = true;
#else
constexpr bool kEspnowSetupStartupEnabled = false;
#endif
#ifdef CONFIG_GATEWAY_SMARTCONFIG_SUPPORTED
constexpr bool kSmartconfigSupported = true;
#else
constexpr bool kSmartconfigSupported = false;
#endif
#ifdef CONFIG_GATEWAY_START_SMARTCONFIG_ENABLED
constexpr bool kSmartconfigStartupEnabled = true;
#else
constexpr bool kSmartconfigStartupEnabled = false;
#endif
#ifdef CONFIG_GATEWAY_USB_STARTUP_SETUP_SERIAL
constexpr bool kUsbSetupStartupEnabled = true;
#else
constexpr bool kUsbSetupStartupEnabled = false;
#endif
std::unique_ptr<gateway::DaliDomainService> s_dali_domain;
std::unique_ptr<gateway::GatewayRuntime> s_runtime;
std::unique_ptr<gateway::GatewayController> s_controller;
std::unique_ptr<gateway::GatewayNetworkService> s_network;
std::unique_ptr<gateway::GatewayBleBridge> s_ble_bridge;
std::unique_ptr<gateway::GatewayUsbSetupBridge> s_usb_setup_bridge;
[[maybe_unused]] void LogBindError(const char* channel_name, esp_err_t err) {
if (err != ESP_OK) {
std::printf("gateway_main: failed to bind %s err=%d\n", channel_name, err);
}
}
struct ChannelBindingConfig {
bool enabled{false};
bool native_phy{false};
bool serial_phy{false};
uint8_t gateway_id{0};
uint8_t native_bus_id{0};
uint32_t native_baudrate{0};
int uart_port{-1};
};
bool ValidateChannelBindings() {
ChannelBindingConfig channels[CONFIG_GATEWAY_CHANNEL_COUNT] = {};
#if CONFIG_GATEWAY_CHANNEL1_PHY_NATIVE
channels[0].enabled = true;
channels[0].native_phy = true;
channels[0].gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_GW_ID);
channels[0].native_bus_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_NATIVE_BUS_ID);
channels[0].native_baudrate = static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL1_NATIVE_BAUDRATE);
#elif CONFIG_GATEWAY_CHANNEL1_PHY_UART1
channels[0].enabled = true;
channels[0].serial_phy = true;
channels[0].gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_GW_ID);
channels[0].uart_port = 1;
#elif CONFIG_GATEWAY_CHANNEL1_PHY_UART2
channels[0].enabled = true;
channels[0].serial_phy = true;
channels[0].gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_GW_ID);
channels[0].uart_port = 2;
#endif
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 2
#if CONFIG_GATEWAY_CHANNEL2_PHY_NATIVE
channels[1].enabled = true;
channels[1].native_phy = true;
channels[1].gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_GW_ID);
channels[1].native_bus_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_NATIVE_BUS_ID);
channels[1].native_baudrate = static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL2_NATIVE_BAUDRATE);
#elif CONFIG_GATEWAY_CHANNEL2_PHY_UART1
channels[1].enabled = true;
channels[1].serial_phy = true;
channels[1].gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_GW_ID);
channels[1].uart_port = 1;
#elif CONFIG_GATEWAY_CHANNEL2_PHY_UART2
channels[1].enabled = true;
channels[1].serial_phy = true;
channels[1].gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_GW_ID);
channels[1].uart_port = 2;
#endif
#endif
bool any_enabled = false;
bool saw_native = false;
uint32_t native_baudrate = 0;
for (int i = 0; i < CONFIG_GATEWAY_CHANNEL_COUNT; ++i) {
if (!channels[i].enabled) {
continue;
}
any_enabled = true;
for (int j = i + 1; j < CONFIG_GATEWAY_CHANNEL_COUNT; ++j) {
if (!channels[j].enabled) {
continue;
}
if (channels[i].gateway_id == channels[j].gateway_id) {
ESP_LOGE(kTag, "duplicate gateway ids configured: %u", channels[i].gateway_id);
return false;
}
if (channels[i].serial_phy && channels[j].serial_phy &&
channels[i].uart_port == channels[j].uart_port) {
ESP_LOGE(kTag, "duplicate serial PHY UART%d configured for multiple channels",
channels[i].uart_port);
return false;
}
if (channels[i].native_phy && channels[j].native_phy &&
channels[i].native_bus_id == channels[j].native_bus_id) {
ESP_LOGE(kTag, "duplicate native DALI bus ids configured: %u",
channels[i].native_bus_id);
return false;
}
}
if (channels[i].native_phy) {
if (!saw_native) {
saw_native = true;
native_baudrate = channels[i].native_baudrate;
} else if (native_baudrate != channels[i].native_baudrate) {
ESP_LOGE(kTag,
"mixed native PHY baudrates are not supported by the shared DALI HAL: %lu vs %lu",
static_cast<unsigned long>(native_baudrate),
static_cast<unsigned long>(channels[i].native_baudrate));
return false;
}
}
}
if (!any_enabled) {
ESP_LOGE(kTag, "no DALI PHY is configured; enable at least one native or serial channel");
return false;
}
return true;
}
esp_err_t BindConfiguredChannels(gateway::DaliDomainService& dali_domain,
const gateway::GatewayRuntime& runtime) {
#if CONFIG_GATEWAY_CHANNEL1_PHY_NATIVE
gateway::DaliHardwareBusConfig channel1{};
channel1.channel_index = 0;
channel1.gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_GW_ID);
channel1.bus_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_NATIVE_BUS_ID);
channel1.tx_pin = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_NATIVE_TX_PIN);
channel1.rx_pin = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_NATIVE_RX_PIN);
channel1.baudrate = static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL1_NATIVE_BAUDRATE);
channel1.name = runtime.gatewayName(channel1.gateway_id);
esp_err_t err = dali_domain.bindHardwareBus(channel1);
LogBindError("channel1 native DALI", err);
if (err != ESP_OK) {
return err;
}
#elif CONFIG_GATEWAY_CHANNEL1_PHY_UART1 || CONFIG_GATEWAY_CHANNEL1_PHY_UART2
gateway::DaliSerialBusConfig channel1{};
channel1.channel_index = 0;
channel1.gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL1_GW_ID);
#if CONFIG_GATEWAY_CHANNEL1_PHY_UART1
channel1.uart_port = 1;
#else
channel1.uart_port = 2;
#endif
channel1.tx_pin = CONFIG_GATEWAY_CHANNEL1_SERIAL_TX_PIN;
channel1.rx_pin = CONFIG_GATEWAY_CHANNEL1_SERIAL_RX_PIN;
channel1.baudrate = static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL1_SERIAL_BAUDRATE);
channel1.rx_buffer_size = static_cast<size_t>(CONFIG_GATEWAY_CHANNEL1_SERIAL_RX_BUFFER);
channel1.tx_buffer_size = static_cast<size_t>(CONFIG_GATEWAY_CHANNEL1_SERIAL_TX_BUFFER);
channel1.query_timeout_ms =
static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL1_SERIAL_QUERY_TIMEOUT_MS);
channel1.name = runtime.gatewayName(channel1.gateway_id);
esp_err_t err = dali_domain.bindSerialBus(channel1);
LogBindError("channel1 serial DALI", err);
if (err != ESP_OK) {
return err;
}
#endif
#if CONFIG_GATEWAY_CHANNEL_COUNT >= 2
#if CONFIG_GATEWAY_CHANNEL2_PHY_NATIVE
gateway::DaliHardwareBusConfig channel2{};
channel2.channel_index = 1;
channel2.gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_GW_ID);
channel2.bus_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_NATIVE_BUS_ID);
channel2.tx_pin = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_NATIVE_TX_PIN);
channel2.rx_pin = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_NATIVE_RX_PIN);
channel2.baudrate = static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL2_NATIVE_BAUDRATE);
channel2.name = runtime.gatewayName(channel2.gateway_id);
esp_err_t err = dali_domain.bindHardwareBus(channel2);
LogBindError("channel2 native DALI", err);
if (err != ESP_OK) {
return err;
}
#elif CONFIG_GATEWAY_CHANNEL2_PHY_UART1 || CONFIG_GATEWAY_CHANNEL2_PHY_UART2
gateway::DaliSerialBusConfig channel2{};
channel2.channel_index = 1;
channel2.gateway_id = static_cast<uint8_t>(CONFIG_GATEWAY_CHANNEL2_GW_ID);
#if CONFIG_GATEWAY_CHANNEL2_PHY_UART1
channel2.uart_port = 1;
#else
channel2.uart_port = 2;
#endif
channel2.tx_pin = CONFIG_GATEWAY_CHANNEL2_SERIAL_TX_PIN;
channel2.rx_pin = CONFIG_GATEWAY_CHANNEL2_SERIAL_RX_PIN;
channel2.baudrate = static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL2_SERIAL_BAUDRATE);
channel2.rx_buffer_size = static_cast<size_t>(CONFIG_GATEWAY_CHANNEL2_SERIAL_RX_BUFFER);
channel2.tx_buffer_size = static_cast<size_t>(CONFIG_GATEWAY_CHANNEL2_SERIAL_TX_BUFFER);
channel2.query_timeout_ms =
static_cast<uint32_t>(CONFIG_GATEWAY_CHANNEL2_SERIAL_QUERY_TIMEOUT_MS);
channel2.name = runtime.gatewayName(channel2.gateway_id);
esp_err_t err = dali_domain.bindSerialBus(channel2);
LogBindError("channel2 serial DALI", err);
if (err != ESP_OK) {
return err;
}
#endif
#endif
return ESP_OK;
}
} // namespace
extern "C" void app_main(void) {
ESP_ERROR_CHECK(gateway::InitializeRuntimeNvs());
const gateway::BootProfile profile{
gateway::AppRole::kGateway,
"gateway",
kWifiSupported,
kBleSupported,
true,
kEspnowSetupSupported,
kUsbSetupStartupEnabled,
};
gateway::GatewayCore core(profile);
core.start();
ESP_ERROR_CHECK(ValidateChannelBindings() ? ESP_OK : ESP_ERR_INVALID_STATE);
s_dali_domain = std::make_unique<gateway::DaliDomainService>();
s_runtime = std::make_unique<gateway::GatewayRuntime>(
profile,
gateway::GatewayRuntimeConfig{
kProjectName,
kProjectVersion,
gateway::ReadRuntimeSerialId(),
kBleStartupEnabled,
},
s_dali_domain.get());
ESP_ERROR_CHECK(s_runtime->start());
s_runtime->setGatewayCount(CONFIG_GATEWAY_CHANNEL_COUNT);
ESP_ERROR_CHECK(BindConfiguredChannels(*s_dali_domain, *s_runtime));
gateway::GatewayControllerConfig controller_config;
controller_config.setup_supported = true;
controller_config.ble_supported = profile.enable_ble;
controller_config.wifi_supported = profile.enable_wifi;
controller_config.ip_router_supported = profile.enable_wifi || profile.enable_eth;
controller_config.internal_scene_supported = true;
controller_config.internal_group_supported = true;
s_controller = std::make_unique<gateway::GatewayController>(*s_runtime, *s_dali_domain,
controller_config);
ESP_ERROR_CHECK(s_controller->start());
if (profile.enable_wifi || profile.enable_eth) {
gateway::GatewayNetworkServiceConfig network_config;
network_config.wifi_enabled = profile.enable_wifi && kWifiStartupEnabled;
network_config.espnow_setup_enabled = profile.enable_espnow;
network_config.espnow_setup_startup_enabled =
profile.enable_espnow && kEspnowSetupStartupEnabled;
network_config.smartconfig_enabled = profile.enable_wifi && kSmartconfigSupported;
network_config.smartconfig_startup_enabled =
profile.enable_wifi && kSmartconfigSupported && kSmartconfigStartupEnabled;
network_config.smartconfig_timeout_sec = CONFIG_GATEWAY_SMARTCONFIG_TIMEOUT_SEC;
#ifdef CONFIG_GATEWAY_NETWORK_HTTP_ENABLED
network_config.http_enabled = true;
#else
network_config.http_enabled = false;
#endif
#ifdef CONFIG_GATEWAY_NETWORK_UDP_ROUTER_ENABLED
network_config.udp_enabled = true;
#else
network_config.udp_enabled = false;
#endif
network_config.http_port = static_cast<uint16_t>(CONFIG_GATEWAY_NETWORK_HTTP_PORT);
network_config.udp_port = static_cast<uint16_t>(CONFIG_GATEWAY_NETWORK_UDP_PORT);
network_config.status_led_gpio = CONFIG_GATEWAY_STATUS_LED_GPIO;
network_config.boot_button_gpio = CONFIG_GATEWAY_BOOT_BUTTON_GPIO;
network_config.boot_button_long_press_ms = CONFIG_GATEWAY_BOOT_BUTTON_LONG_PRESS_MS;
#ifdef CONFIG_GATEWAY_STATUS_LED_ACTIVE_HIGH
network_config.status_led_active_high = true;
#else
network_config.status_led_active_high = false;
#endif
#ifdef CONFIG_GATEWAY_BOOT_BUTTON_ACTIVE_LOW
network_config.boot_button_active_low = true;
#else
network_config.boot_button_active_low = false;
#endif
s_network = std::make_unique<gateway::GatewayNetworkService>(*s_controller, *s_runtime,
*s_dali_domain, network_config);
ESP_ERROR_CHECK(s_network->start());
}
if (profile.enable_ble) {
s_ble_bridge = std::make_unique<gateway::GatewayBleBridge>(*s_controller, *s_runtime,
*s_dali_domain);
ESP_ERROR_CHECK(s_ble_bridge->start());
}
if (profile.enable_usb) {
gateway::GatewayUsbSetupBridgeConfig usb_config;
usb_config.enabled = true;
usb_config.channel_index = static_cast<uint8_t>(CONFIG_GATEWAY_USB_SETUP_CHANNEL_INDEX);
usb_config.rx_buffer_size = static_cast<size_t>(CONFIG_GATEWAY_USB_SETUP_RX_BUFFER);
usb_config.tx_buffer_size = static_cast<size_t>(CONFIG_GATEWAY_USB_SETUP_TX_BUFFER);
usb_config.read_timeout_ms = static_cast<uint32_t>(CONFIG_GATEWAY_USB_SETUP_READ_TIMEOUT_MS);
s_usb_setup_bridge = std::make_unique<gateway::GatewayUsbSetupBridge>(*s_controller,
*s_dali_domain,
usb_config);
ESP_ERROR_CHECK(s_usb_setup_bridge->start());
}
const auto device_info = s_runtime->deviceInfo();
std::printf("gateway_main: dali domain implementation=%s bound=%d channels=%u\n",
s_dali_domain->implementationName(), s_dali_domain->isBound(),
static_cast<unsigned>(s_dali_domain->channelCount()));
for (const auto& channel : s_dali_domain->channelInfo()) {
std::printf("gateway_main: channel=%u gateway=%u name=%s\n", channel.channel_index,
channel.gateway_id, channel.name.c_str());
}
std::printf("gateway_main: runtime device type=%s serial=%s project=%s version=%s\n",
device_info.type.c_str(), device_info.serial_id.c_str(),
device_info.project.c_str(), device_info.version.c_str());
}