feat: Implement Homeassistant MQTT Gateway
This commit transforms the previous "Blinky" sample into a functional
Homeassistant MQTT Gateway application.
Key features and changes include:
- **Application Logic:** The core application logic is updated to function as a gateway between Modbus and MQTT.
- **Network Configuration:**
- Implemented persistent network settings using the Zephyr settings subsystem and NVS.
- Added robust Wi-Fi connection management with auto-reconnect.
- Centralized IP configuration to support both DHCP and static IP addresses.
- **Configuration:** Project and board configurations have been updated to enable necessary kernel features and network settings.
- **Documentation:** The README has been updated to reflect the new functionality and usage instructions.
- **Simulation:** The simulation script has been updated.
This commit is contained in:
parent
cbbd5f5fea
commit
6e669cfc4e
116
README.rst
116
README.rst
|
|
@ -1,97 +1,65 @@
|
||||||
.. zephyr:code-sample:: blinky
|
.. SPDX-License-Identifier: Apache-2.0
|
||||||
:name: Blinky
|
|
||||||
:relevant-api: gpio_interface
|
|
||||||
|
|
||||||
Blink an LED forever using the GPIO API.
|
############################
|
||||||
|
Modbus MQTT Gateway
|
||||||
|
############################
|
||||||
|
|
||||||
Overview
|
Overview
|
||||||
********
|
********
|
||||||
|
|
||||||
The Blinky sample blinks an LED forever using the :ref:`GPIO API <gpio_api>`.
|
This Zephyr application serves as an MQTT gateway, primarily designed to bridge Modbus devices with a Home Assistant instance. It connects to a WiFi network, obtains an IP address, and communicates with an MQTT broker.
|
||||||
|
|
||||||
The source code shows how to:
|
Features
|
||||||
|
********
|
||||||
|
|
||||||
#. Get a pin specification from the :ref:`devicetree <dt-guide>` as a
|
- **WiFi Connectivity**: Connects to a specified WiFi network using credentials provided via Kconfig.
|
||||||
:c:struct:`gpio_dt_spec`
|
- **Network Configuration**: Supports both DHCP and static IP addressing for flexible network integration.
|
||||||
#. Configure the GPIO pin as an output
|
- **MQTT Integration**: Configured to communicate with an MQTT broker, with specific options for Home Assistant discovery.
|
||||||
#. Toggle the pin forever
|
- **Configurability**: All major parameters, including WiFi, network, and MQTT settings, are configurable through Kconfig.
|
||||||
|
- **Simulation Support**: Includes a script and configuration for running in a simulated environment (`native_sim`).
|
||||||
|
|
||||||
See :zephyr:code-sample:`pwm-blinky` for a similar sample that uses the PWM API instead.
|
Configuration
|
||||||
|
*************
|
||||||
|
|
||||||
.. _blinky-sample-requirements:
|
The application is configured using Kconfig. Key options can be found in the "Home Assistant MQTT Options" menu.
|
||||||
|
|
||||||
Requirements
|
- **WiFi Settings**:
|
||||||
************
|
- `CONFIG_WIFI_SSID`: The SSID of the WiFi network.
|
||||||
|
- `CONFIG_WIFI_PASSWORD`: The password for the WiFi network.
|
||||||
|
|
||||||
Your board must:
|
- **Network Settings**:
|
||||||
|
- `CONFIG_NET_DHCP`: Enable or disable DHCP.
|
||||||
|
- `CONFIG_NET_IP_ADDR`: Static IP address.
|
||||||
|
- `CONFIG_NET_IP_MASK`: Static IP netmask.
|
||||||
|
- `CONFIG_NET_IP_GATEWAY`: Static IP gateway.
|
||||||
|
- `CONFIG_NET_DNS_SERVER`: Static DNS server.
|
||||||
|
|
||||||
#. Have an LED connected via a GPIO pin (these are called "User LEDs" on many of
|
- **MQTT Broker Settings**:
|
||||||
Zephyr's :ref:`boards`).
|
- `CONFIG_HA_MQTT_BROKER_HOSTNAME`: Hostname or IP of the MQTT broker.
|
||||||
#. Have the LED configured using the ``led0`` devicetree alias.
|
- `CONFIG_HA_MQTT_BROKER_PORT`: Port of the MQTT broker.
|
||||||
|
- `CONFIG_HA_MQTT_USERNAME`: Username for MQTT authentication.
|
||||||
|
- `CONFIG_HA_MQTT_PASSWORD`: Password for MQTT authentication.
|
||||||
|
|
||||||
|
- **Home Assistant Device Info**:
|
||||||
|
- `CONFIG_HA_MQTT_NAME`: Device name.
|
||||||
|
- `CONFIG_HA_MQTT_MANUFACTURER`: Manufacturer name.
|
||||||
|
- `CONFIG_HA_MQTT_MODEL`: Model name.
|
||||||
|
|
||||||
Building and Running
|
Building and Running
|
||||||
********************
|
********************
|
||||||
|
|
||||||
Build and flash Blinky as follows, changing ``reel_board`` for your board:
|
**Building for Hardware (e.g., ESP32-C6)**
|
||||||
|
|
||||||
.. zephyr-app-commands::
|
.. code-block:: shell
|
||||||
:zephyr-app: samples/basic/blinky
|
|
||||||
:board: reel_board
|
|
||||||
:goals: build flash
|
|
||||||
:compact:
|
|
||||||
|
|
||||||
After flashing, the LED starts to blink and messages with the current LED state
|
west build -b esp32c6_devkitc_hpcore
|
||||||
are printed on the console. If a runtime error occurs, the sample exits without
|
|
||||||
printing to the console.
|
|
||||||
|
|
||||||
Build errors
|
**Running in Simulation**
|
||||||
************
|
|
||||||
|
|
||||||
You will see a build error at the source code line defining the ``struct
|
A simulation can be run using the provided script:
|
||||||
gpio_dt_spec led`` variable if you try to build Blinky for an unsupported
|
|
||||||
board.
|
|
||||||
|
|
||||||
On GCC-based toolchains, the error looks like this:
|
.. code-block:: shell
|
||||||
|
|
||||||
.. code-block:: none
|
./run_sim.sh
|
||||||
|
|
||||||
error: '__device_dts_ord_DT_N_ALIAS_led_P_gpios_IDX_0_PH_ORD' undeclared here (not in a function)
|
This will build and run the application for the `native_sim` target.
|
||||||
|
|
||||||
Adding board support
|
|
||||||
********************
|
|
||||||
|
|
||||||
To add support for your board, add something like this to your devicetree:
|
|
||||||
|
|
||||||
.. code-block:: DTS
|
|
||||||
|
|
||||||
/ {
|
|
||||||
aliases {
|
|
||||||
led0 = &myled0;
|
|
||||||
};
|
|
||||||
|
|
||||||
leds {
|
|
||||||
compatible = "gpio-leds";
|
|
||||||
myled0: led_0 {
|
|
||||||
gpios = <&gpio0 13 GPIO_ACTIVE_LOW>;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
The above sets your board's ``led0`` alias to use pin 13 on GPIO controller
|
|
||||||
``gpio0``. The pin flags :c:macro:`GPIO_ACTIVE_HIGH` mean the LED is on when
|
|
||||||
the pin is set to its high state, and off when the pin is in its low state.
|
|
||||||
|
|
||||||
Tips:
|
|
||||||
|
|
||||||
- See :dtcompatible:`gpio-leds` for more information on defining GPIO-based LEDs
|
|
||||||
in devicetree.
|
|
||||||
|
|
||||||
- If you're not sure what to do, check the devicetrees for supported boards which
|
|
||||||
use the same SoC as your target. See :ref:`get-devicetree-outputs` for details.
|
|
||||||
|
|
||||||
- See :zephyr_file:`include/zephyr/dt-bindings/gpio/gpio.h` for the flags you can use
|
|
||||||
in devicetree.
|
|
||||||
|
|
||||||
- If the LED is built in to your board hardware, the alias should be defined in
|
|
||||||
your :ref:`BOARD.dts file <devicetree-in-out-files>`. Otherwise, you can
|
|
||||||
define one in a :ref:`devicetree overlay <set-devicetree-overlays>`.
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
CONFIG_WIFI=y
|
CONFIG_WIFI=y
|
||||||
CONFIG_WIFI_ESP32=y
|
CONFIG_NET_L2_WIFI_SHELL=y
|
||||||
CONFIG_ESP32_WIFI_STA_RECONNECT=y
|
CONFIG_WIFI_ESP32=y
|
||||||
21
prj.conf
21
prj.conf
|
|
@ -1,3 +1,23 @@
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# APP SETTINGS
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# See KConfig documentation for more details on these settings.
|
||||||
|
# Most of them can also be set in the shell.
|
||||||
|
CONFIG_NET_DHCP=n
|
||||||
|
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# GENERAL SETTINGS
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
# Settings subsystem
|
||||||
|
CONFIG_SETTINGS=y
|
||||||
|
CONFIG_FLASH=y
|
||||||
|
CONFIG_FLASH_MAP=y
|
||||||
|
CONFIG_FLASH_PAGE_LAYOUT=y
|
||||||
|
CONFIG_SETTINGS_NVS=y
|
||||||
|
CONFIG_NVS=y
|
||||||
|
CONFIG_SETTINGS_RUNTIME=y
|
||||||
|
|
||||||
# Networking core
|
# Networking core
|
||||||
CONFIG_NETWORKING=y
|
CONFIG_NETWORKING=y
|
||||||
CONFIG_NET_IPV4=y
|
CONFIG_NET_IPV4=y
|
||||||
|
|
@ -16,6 +36,7 @@ CONFIG_DNS_RESOLVER_MAX_SERVERS=2
|
||||||
CONFIG_NET_LOG=y
|
CONFIG_NET_LOG=y
|
||||||
CONFIG_LOG=y
|
CONFIG_LOG=y
|
||||||
CONFIG_NET_SHELL=y
|
CONFIG_NET_SHELL=y
|
||||||
|
CONFIG_REBOOT=y
|
||||||
|
|
||||||
# Debugging and stack
|
# Debugging and stack
|
||||||
CONFIG_INIT_STACKS=y
|
CONFIG_INIT_STACKS=y
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
while true; do
|
while true; do
|
||||||
# build/zephyr/zephyr.exe --flash=flash.bin --uart_stdinout
|
build/zephyr/zephyr.exe --flash=flash.bin --uart_stdinout
|
||||||
build/zephyr/zephyr.exe --uart_stdinout
|
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
break
|
break
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ LOG_MODULE_REGISTER(mqtt_gw, CONFIG_LOG_DEFAULT_LEVEL);
|
||||||
* @return 0 on success, otherwise a negative error code.
|
* @return 0 on success, otherwise a negative error code.
|
||||||
*/
|
*/
|
||||||
int main(void) {
|
int main(void) {
|
||||||
// k_sleep(K_SECONDS(1)); // Allow time for logging initialization
|
k_sleep(K_MSEC(100)); // Allow time for logging initialization
|
||||||
LOG_INF("MQTT Gateway. Board: %s", CONFIG_BOARD);
|
LOG_INF("MQTT Gateway. Board: %s", CONFIG_BOARD);
|
||||||
net_init();
|
net_init();
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
||||||
576
src/net.c
576
src/net.c
|
|
@ -12,14 +12,35 @@ LOG_MODULE_REGISTER(net, LOG_LEVEL_DBG);
|
||||||
#include <zephyr/net/net_if.h>
|
#include <zephyr/net/net_if.h>
|
||||||
#include <zephyr/net/net_mgmt.h>
|
#include <zephyr/net/net_mgmt.h>
|
||||||
|
|
||||||
|
#include <zephyr/settings/settings.h>
|
||||||
|
#include <zephyr/shell/shell.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#ifdef CONFIG_WIFI
|
#ifdef CONFIG_WIFI
|
||||||
#include <zephyr/net/wifi_mgmt.h>
|
#include <zephyr/net/wifi_mgmt.h>
|
||||||
static struct net_mgmt_event_callback wifi_mgmt_cb;
|
|
||||||
static K_SEM_DEFINE(wifi_connected_sem, 0, 1);
|
#define WIFI_RECONNECT_DELAY_S 10 // Wartezeit in Sekunden
|
||||||
|
|
||||||
|
static struct net_mgmt_event_callback wifi_connect_cb;
|
||||||
|
static struct net_mgmt_event_callback wifi_disconnect_cb;
|
||||||
|
|
||||||
|
static void attempt_wifi_reconnect_work_handler(struct k_work *work);
|
||||||
|
static struct k_timer wifi_reconnect_timer;
|
||||||
|
static K_WORK_DEFINE(reconnect_work, attempt_wifi_reconnect_work_handler);
|
||||||
|
|
||||||
#endif // CONFIG_WIFI
|
#endif // CONFIG_WIFI
|
||||||
|
|
||||||
static struct net_mgmt_event_callback net_mgmt_cb;
|
|
||||||
|
|
||||||
|
// Hooks for MQTT client control
|
||||||
|
__weak void on_network_ready(struct net_if *iface)
|
||||||
|
{
|
||||||
|
LOG_INF("Network is ready for communication (default weak handler)");
|
||||||
|
}
|
||||||
|
|
||||||
|
__weak void on_network_down(struct net_if *iface)
|
||||||
|
{
|
||||||
|
LOG_INF("Network is down (default weak handler)");
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_NET_DHCP
|
#ifdef CONFIG_NET_DHCP
|
||||||
static bool dhcp_enabled = true;
|
static bool dhcp_enabled = true;
|
||||||
|
|
@ -32,249 +53,376 @@ static char *static_netmask = CONFIG_NET_IP_MASK;
|
||||||
static char *static_gateway = CONFIG_NET_IP_GATEWAY;
|
static char *static_gateway = CONFIG_NET_IP_GATEWAY;
|
||||||
static char *static_dns = CONFIG_NET_DNS_SERVER;
|
static char *static_dns = CONFIG_NET_DNS_SERVER;
|
||||||
|
|
||||||
/**
|
static int net_settings_handle(
|
||||||
* @brief Starts the DHCPv4 client on the given network interface.
|
const char *name, size_t len, settings_read_cb read_cb, void *cb_arg);
|
||||||
*
|
|
||||||
* @param iface The network interface.
|
struct settings_handler net_settings = {
|
||||||
* @param user_data A pointer to user data (unused).
|
.name = "net",
|
||||||
*/
|
.h_set = net_settings_handle,
|
||||||
static void start_dhcpv4_client(struct net_if *iface, void *user_data)
|
};
|
||||||
|
|
||||||
|
static int net_settings_handle(const char *name, size_t len, settings_read_cb read_cb, void *cb_arg)
|
||||||
{
|
{
|
||||||
ARG_UNUSED(user_data);
|
const char *next;
|
||||||
if (!iface) {
|
int rc;
|
||||||
LOG_ERR("No network interface provided");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!net_if_is_up(iface)) {
|
if (settings_name_steq(name, "dhcp", &next) && !next) {
|
||||||
LOG_ERR("Network interface %s is not up", net_if_get_device(iface)->name);
|
rc = read_cb(cb_arg, &dhcp_enabled, sizeof(dhcp_enabled));
|
||||||
return;
|
if (rc < 0) {
|
||||||
}
|
LOG_ERR("Error reading setting 'dhcp_enabled' (%d)", rc);
|
||||||
|
} else {
|
||||||
LOG_INF("Starting DHCPv4 client on interface: %s, index %d",
|
LOG_DBG("Loaded 'dhcp_enabled' from settings: %s",
|
||||||
net_if_get_device(iface)->name,
|
dhcp_enabled ? "ENABLED" : "DISABLED");
|
||||||
net_if_get_by_iface(iface));
|
}
|
||||||
net_dhcpv4_start(iface);
|
return rc > 0 ? 0 : rc;
|
||||||
|
}
|
||||||
|
return -ENOENT;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
int net_settings_init(void)
|
||||||
* @brief Network management event handler.
|
|
||||||
*
|
|
||||||
* This function is called when a network management event occurs. It logs
|
|
||||||
* information about the new IP address, subnet mask, router, and lease time
|
|
||||||
* when an IPv4 address is added.
|
|
||||||
*
|
|
||||||
* @param cb The network management event callback structure.
|
|
||||||
* @param mgmt_event The network management event.
|
|
||||||
* @param iface The network interface.
|
|
||||||
*/
|
|
||||||
static void net_mgmt_event_handler(
|
|
||||||
struct net_mgmt_event_callback *cb, uint64_t mgmt_event, struct net_if *iface)
|
|
||||||
{
|
{
|
||||||
if (mgmt_event == NET_EVENT_IPV4_ADDR_ADD) {
|
int rc;
|
||||||
for (int i = 0; i < NET_IF_MAX_IPV4_ADDR; i++) {
|
rc = settings_subsys_init();
|
||||||
char buf[NET_IPV4_ADDR_LEN];
|
if (rc < 0) {
|
||||||
|
LOG_ERR("Failed to initialize settings subsystem: %d", rc);
|
||||||
if (iface->config.ip.ipv4->unicast[i].ipv4.addr_type != NET_ADDR_DHCP) {
|
return rc;
|
||||||
continue;
|
}
|
||||||
}
|
rc = settings_register(&net_settings);
|
||||||
|
if (rc < 0) {
|
||||||
LOG_INF(" Address[%d]: %s",
|
LOG_ERR("Failed to register settings handler: %d", rc);
|
||||||
net_if_get_by_iface(iface),
|
return rc;
|
||||||
net_addr_ntop(AF_INET,
|
}
|
||||||
&iface->config.ip.ipv4->unicast[i].ipv4.address.in_addr,
|
rc = settings_load();
|
||||||
buf,
|
if (rc < 0) {
|
||||||
sizeof(buf)));
|
LOG_ERR("Failed to load settings: %d", rc);
|
||||||
LOG_INF(" Subnet[%d]: %s",
|
return rc;
|
||||||
net_if_get_by_iface(iface),
|
}
|
||||||
net_addr_ntop(AF_INET,
|
return 0;
|
||||||
&iface->config.ip.ipv4->unicast[i].netmask,
|
|
||||||
buf,
|
|
||||||
sizeof(buf)));
|
|
||||||
LOG_INF(" Router[%d]: %s",
|
|
||||||
net_if_get_by_iface(iface),
|
|
||||||
net_addr_ntop(AF_INET, &iface->config.ip.ipv4->gw, buf, sizeof(buf)));
|
|
||||||
LOG_INF("Lease time[%d]: %u seconds",
|
|
||||||
net_if_get_by_iface(iface),
|
|
||||||
iface->config.dhcpv4.lease_time);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_WIFI
|
static void net_log_ip_settings(struct net_if *iface)
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Wi-Fi management event handler.
|
|
||||||
*
|
|
||||||
* This function is called when a Wi-Fi management event occurs. It gives the
|
|
||||||
* `wifi_connected_sem` semaphore when the Wi-Fi is connected.
|
|
||||||
*
|
|
||||||
* @param cb The network management event callback structure.
|
|
||||||
* @param mgmt_event The network management event.
|
|
||||||
* @param iface The network interface.
|
|
||||||
*/
|
|
||||||
static void wifi_mgmt_event_handler(struct net_mgmt_event_callback *cb,
|
|
||||||
uint64_t mgmt_event, struct net_if *iface)
|
|
||||||
{
|
{
|
||||||
if (mgmt_event == NET_EVENT_WIFI_CONNECT_RESULT) {
|
char buf[NET_IPV4_ADDR_LEN];
|
||||||
LOG_INF("Wi-Fi connected!");
|
struct net_if_ipv4 *ipv4 = iface->config.ip.ipv4;
|
||||||
k_sem_give(&wifi_connected_sem);
|
if (!ipv4) {
|
||||||
|
LOG_INF("No IPv4 config on interface %s", net_if_get_device(iface)->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
LOG_INF("IP settings for interface: %s", net_if_get_device(iface)->name);
|
||||||
|
LOG_INF(" IP: %s",
|
||||||
|
net_addr_ntop(AF_INET, &ipv4->unicast[0].ipv4.address.in_addr, buf, sizeof(buf)));
|
||||||
|
LOG_INF(
|
||||||
|
" Netmask: %s", net_addr_ntop(AF_INET, &ipv4->unicast[0].netmask, buf, sizeof(buf)));
|
||||||
|
LOG_INF(" Gateway: %s", net_addr_ntop(AF_INET, &ipv4->gw, buf, sizeof(buf)));
|
||||||
|
if (static_dns) {
|
||||||
|
LOG_INF(" DNS: %s", static_dns);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
static void start_dhcpv4_client(struct net_if *iface, void *user_data)
|
||||||
* @brief Initializes the Wi-Fi connection.
|
|
||||||
*
|
|
||||||
* This function sets up the Wi-Fi connection parameters and starts the
|
|
||||||
* connection process. It registers an event handler to listen for connection
|
|
||||||
* results.
|
|
||||||
*/
|
|
||||||
void init_wifi(void)
|
|
||||||
{
|
{
|
||||||
// Netzwerk-Interface abrufen
|
ARG_UNUSED(user_data);
|
||||||
struct net_if *iface = net_if_get_default();
|
|
||||||
if (!iface) {
|
if (!iface) {
|
||||||
LOG_ERR("Could not get network interface");
|
LOG_ERR("No network interface provided");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Wi-Fi-Verbindungsparameter
|
if (!net_if_is_up(iface)) {
|
||||||
|
LOG_ERR("Network interface %s is not up", net_if_get_device(iface)->name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INF("Starting DHCPv4 client on interface: %s, index %d",
|
||||||
|
net_if_get_device(iface)->name,
|
||||||
|
net_if_get_by_iface(iface));
|
||||||
|
net_dhcpv4_start(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void net_event_ipv4_addr_add_handler(
|
||||||
|
struct net_mgmt_event_callback *cb, uint64_t mgmt_event, struct net_if *iface)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(cb);
|
||||||
|
ARG_UNUSED(mgmt_event);
|
||||||
|
LOG_INF("#### IPV4 address assigned");
|
||||||
|
net_log_ip_settings(iface);
|
||||||
|
on_network_ready(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void net_event_if_up_handler(
|
||||||
|
struct net_mgmt_event_callback *cb, uint64_t mgmt_event, struct net_if *iface)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(cb);
|
||||||
|
ARG_UNUSED(mgmt_event);
|
||||||
|
LOG_INF("#### Network interface %s is UP", net_if_get_device(iface)->name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void net_event_if_down_handler(
|
||||||
|
struct net_mgmt_event_callback *cb, uint64_t mgmt_event, struct net_if *iface)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(cb);
|
||||||
|
ARG_UNUSED(mgmt_event);
|
||||||
|
LOG_INF("####Network interface %s is DOWN", net_if_get_device(iface)->name);
|
||||||
|
on_network_down(iface);
|
||||||
|
}
|
||||||
|
|
||||||
|
int net_set_ip(void) {
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (dhcp_enabled) {
|
||||||
|
LOG_INF("DHCP is enabled, starting DHCP client on all interfaces");
|
||||||
|
net_if_foreach(start_dhcpv4_client, NULL);
|
||||||
|
} else {
|
||||||
|
struct net_if *iface = net_if_get_default();
|
||||||
|
if (!iface) {
|
||||||
|
LOG_ERR("No default network interface found");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
LOG_INF("DHCP is disabled, setting static IP configuration on interface: %s",
|
||||||
|
net_if_get_device(iface)->name);
|
||||||
|
|
||||||
|
struct in_addr ipaddr, netmask, gw;
|
||||||
|
|
||||||
|
if (net_addr_pton(AF_INET, static_ip, &ipaddr) < 0 ||
|
||||||
|
net_addr_pton(AF_INET, static_netmask, &netmask) < 0 ||
|
||||||
|
net_addr_pton(AF_INET, static_gateway, &gw) < 0) {
|
||||||
|
LOG_ERR("Invalid static IP configuration");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct net_if_addr *ifaddr =
|
||||||
|
net_if_ipv4_addr_add(iface, &ipaddr, NET_ADDR_MANUAL, 0);
|
||||||
|
if (ifaddr == NULL) {
|
||||||
|
LOG_ERR("Failed to add IPv4 address");
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
rc = net_if_ipv4_set_netmask_by_addr(iface, &ipaddr, &netmask);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOG_ERR("Failed to set netmask: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
net_if_ipv4_set_gw(iface, &gw);
|
||||||
|
|
||||||
|
const char *dns_servers_str[] = {
|
||||||
|
static_dns,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
struct sockaddr_in dns_server;
|
||||||
|
dns_server.sin_family = AF_INET;
|
||||||
|
dns_server.sin_port = htons(53);
|
||||||
|
rc = net_addr_pton(AF_INET, static_dns, &dns_server.sin_addr);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOG_ERR("Invalid DNS server address");
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
const struct sockaddr *dns_servers_sa[] = {
|
||||||
|
(struct sockaddr *)&dns_server,
|
||||||
|
NULL,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct dns_resolve_context dns_ctx;
|
||||||
|
rc = dns_resolve_init(&dns_ctx, dns_servers_str, dns_servers_sa);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOG_ERR("Failed to add DNS server: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!net_if_is_up(iface)) {
|
||||||
|
rc = net_if_up(iface);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOG_ERR("Failed to bring up network interface %s: %d",
|
||||||
|
net_if_get_device(iface)->name,
|
||||||
|
rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
LOG_INF("Static IP configuration set");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_WIFI
|
||||||
|
static void schedule_reconnect(void)
|
||||||
|
{
|
||||||
|
LOG_INF("Scheduling Wi-Fi reconnect in %d seconds.", WIFI_RECONNECT_DELAY_S);
|
||||||
|
k_timer_start(&wifi_reconnect_timer, K_SECONDS(WIFI_RECONNECT_DELAY_S), K_NO_WAIT);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wifi_connect_handler(
|
||||||
|
struct net_mgmt_event_callback *cb, uint64_t mgmt_event, struct net_if *iface)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(mgmt_event);
|
||||||
|
ARG_UNUSED(iface);
|
||||||
|
|
||||||
|
const int status = *(const int *)cb->info;
|
||||||
|
|
||||||
|
if (status != 0) {
|
||||||
|
LOG_ERR("Wi-Fi connection attempt failed with status: %d", status);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INF("Wi-Fi connected successfully! Stopping reconnect timer.");
|
||||||
|
k_timer_stop(&wifi_reconnect_timer);
|
||||||
|
|
||||||
|
net_set_ip();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void wifi_disconnect_handler(
|
||||||
|
struct net_mgmt_event_callback *cb, uint64_t mgmt_event, struct net_if *iface)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(cb);
|
||||||
|
ARG_UNUSED(mgmt_event);
|
||||||
|
|
||||||
|
LOG_WRN("Wi-Fi disconnected!");
|
||||||
|
net_dhcpv4_stop(iface);
|
||||||
|
on_network_down(iface);
|
||||||
|
schedule_reconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
int net_wifi_connect(struct net_if *iface, struct wifi_connect_req_params *cnx_params)
|
||||||
|
{
|
||||||
|
int rc;
|
||||||
|
LOG_INF("Connecting to Wi-Fi...");
|
||||||
|
rc = net_mgmt(NET_REQUEST_WIFI_CONNECT,
|
||||||
|
iface,
|
||||||
|
cnx_params,
|
||||||
|
sizeof(struct wifi_connect_req_params));
|
||||||
|
if (rc != 0) {
|
||||||
|
LOG_ERR("Wi-Fi connection request failed with error: %d", rc);
|
||||||
|
} else {
|
||||||
|
LOG_INF("Wi-Fi connection request sent.");
|
||||||
|
}
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int init_wifi(void)
|
||||||
|
{
|
||||||
|
struct net_if *iface = net_if_get_default();
|
||||||
|
if (!iface) {
|
||||||
|
LOG_ERR("Could not get network interface");
|
||||||
|
return -ENODEV;
|
||||||
|
}
|
||||||
|
|
||||||
struct wifi_connect_req_params cnx_params = {
|
struct wifi_connect_req_params cnx_params = {
|
||||||
.ssid = "ItenIOT", // Ersetzen Sie dies mit Ihrer SSID
|
.ssid = "ItenIOT",
|
||||||
.ssid_length = sizeof("ItenIOT") - 1,
|
.ssid_length = sizeof("ItenIOT") - 1,
|
||||||
.psk = "DasPferd", // Ersetzen Sie dies mit Ihrem Passwort
|
.psk = "DasPferd",
|
||||||
.psk_length = sizeof("DasPferd") - 1,
|
.psk_length = sizeof("DasPferd") - 1,
|
||||||
.security = WIFI_SECURITY_TYPE_PSK,
|
.security = WIFI_SECURITY_TYPE_PSK,
|
||||||
.channel = WIFI_CHANNEL_ANY,
|
.channel = WIFI_CHANNEL_ANY,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Event-Handler registrieren
|
static bool wifi_handlers_registered = false;
|
||||||
net_mgmt_init_event_callback(&wifi_mgmt_cb, wifi_mgmt_event_handler, NET_EVENT_WIFI_CONNECT_RESULT);
|
if (!wifi_handlers_registered) {
|
||||||
net_mgmt_add_event_callback(&wifi_mgmt_cb);
|
net_mgmt_init_event_callback(
|
||||||
|
&wifi_connect_cb, wifi_connect_handler, NET_EVENT_WIFI_CONNECT_RESULT);
|
||||||
LOG_INF("Connecting to Wi-Fi...");
|
net_mgmt_add_event_callback(&wifi_connect_cb);
|
||||||
|
net_mgmt_init_event_callback(
|
||||||
// Verbindungsanfrage senden
|
&wifi_disconnect_cb, wifi_disconnect_handler, NET_EVENT_WIFI_DISCONNECT_RESULT);
|
||||||
if (net_mgmt(NET_REQUEST_WIFI_CONNECT, iface, &cnx_params, sizeof(struct wifi_connect_req_params))) {
|
net_mgmt_add_event_callback(&wifi_disconnect_cb);
|
||||||
LOG_ERR("Wi-Fi connection request failed");
|
wifi_handlers_registered = true;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Warten, bis die Verbindung hergestellt ist
|
return net_wifi_connect(iface, &cnx_params);
|
||||||
if (k_sem_take(&wifi_connected_sem, K_SECONDS(30)) != 0) {
|
|
||||||
LOG_ERR("Wi-Fi connection timed out");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ab hier ist die Wi-Fi-Verbindung aufgebaut und Sie können Netzwerkoperationen durchführen.
|
|
||||||
// Ein DHCP-Client wird automatisch gestartet, um eine IP-Adresse zu beziehen.
|
|
||||||
LOG_INF("Wi-Fi setup complete.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void attempt_wifi_reconnect_work_handler(struct k_work *work)
|
||||||
|
{
|
||||||
|
LOG_INF("Reconnect timer expired. Trying to connect...");
|
||||||
|
if (init_wifi() != 0) {
|
||||||
|
LOG_ERR("Failed to send Wi-Fi connection request, will try again.");
|
||||||
|
schedule_reconnect();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void on_reconnect_timer_expiry(struct k_timer *timer_id)
|
||||||
|
{
|
||||||
|
k_work_submit(&reconnect_work);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // CONFIG_WIFI
|
#endif // CONFIG_WIFI
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Initializes the network stack.
|
|
||||||
*
|
|
||||||
* This function initializes the network stack. It initializes and adds a
|
|
||||||
* network management event callback to log network events. If Wi-Fi is
|
|
||||||
* enabled, it initializes the Wi-Fi connection. If DHCP is enabled, it
|
|
||||||
* starts the DHCP client on all interfaces. Otherwise, it sets a static IP
|
|
||||||
* configuration.
|
|
||||||
*
|
|
||||||
* @return 0 on success, otherwise a negative error code.
|
|
||||||
*/
|
|
||||||
int net_init(void)
|
int net_init(void)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
net_mgmt_init_event_callback(&net_mgmt_cb, net_mgmt_event_handler, NET_EVENT_IPV4_ADDR_ADD);
|
|
||||||
net_mgmt_add_event_callback(&net_mgmt_cb);
|
rc = net_settings_init();
|
||||||
|
if (rc < 0) {
|
||||||
|
LOG_ERR("Failed to initialize network settings: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct net_mgmt_event_callback net_mgmt_cb_ipv4_addr_add;
|
||||||
|
static struct net_mgmt_event_callback net_mgmt_cb_if_up;
|
||||||
|
static struct net_mgmt_event_callback net_mgmt_cb_if_down;
|
||||||
|
|
||||||
|
net_mgmt_init_event_callback(
|
||||||
|
&net_mgmt_cb_ipv4_addr_add, net_event_ipv4_addr_add_handler, NET_EVENT_IPV4_ADDR_ADD);
|
||||||
|
net_mgmt_add_event_callback(&net_mgmt_cb_ipv4_addr_add);
|
||||||
|
net_mgmt_init_event_callback(&net_mgmt_cb_if_up, net_event_if_up_handler, NET_EVENT_IF_UP);
|
||||||
|
net_mgmt_add_event_callback(&net_mgmt_cb_if_up);
|
||||||
|
net_mgmt_init_event_callback(
|
||||||
|
&net_mgmt_cb_if_down, net_event_if_down_handler, NET_EVENT_IF_DOWN);
|
||||||
|
net_mgmt_add_event_callback(&net_mgmt_cb_if_down);
|
||||||
|
|
||||||
#ifdef CONFIG_WIFI
|
#ifdef CONFIG_WIFI
|
||||||
init_wifi();
|
k_timer_init(&wifi_reconnect_timer, on_reconnect_timer_expiry, NULL);
|
||||||
|
|
||||||
|
rc = init_wifi();
|
||||||
|
if (rc != 0) {
|
||||||
|
LOG_ERR("Initial Wi-Fi connection request failed: %d", rc);
|
||||||
|
schedule_reconnect();
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
rc = net_set_ip();
|
||||||
|
if (rc < 0) {
|
||||||
|
LOG_ERR("Failed to set IP: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
#endif // CONFIG_WIFI
|
#endif // CONFIG_WIFI
|
||||||
|
|
||||||
if (dhcp_enabled) {
|
return 0;
|
||||||
LOG_INF("DHCP is enabled, starting DHCP client on all interfaces");
|
|
||||||
net_if_foreach(start_dhcpv4_client, NULL);
|
|
||||||
} else {
|
|
||||||
struct net_if *iface = net_if_get_default();
|
|
||||||
if (!iface) {
|
|
||||||
LOG_ERR("No default network interface found");
|
|
||||||
return -ENODEV;
|
|
||||||
}
|
|
||||||
LOG_INF("DHCP is disabled, setting static IP configuration on interface: %s",
|
|
||||||
net_if_get_device(iface)->name);
|
|
||||||
if (net_if_is_up(iface)) {
|
|
||||||
LOG_INF("Bringing down network interface %s before setting static IP",
|
|
||||||
net_if_get_device(iface)->name);
|
|
||||||
// Ensure the interface is down before applying static configuration
|
|
||||||
rc = net_if_down(iface);
|
|
||||||
if (rc < 0) {
|
|
||||||
LOG_ERR("Failed to bring down network interface %s: %d",
|
|
||||||
net_if_get_device(iface)->name,
|
|
||||||
rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
struct in_addr ipaddr, netmask, gw;
|
|
||||||
|
|
||||||
if (net_addr_pton(AF_INET, static_ip, &ipaddr) < 0 ||
|
|
||||||
net_addr_pton(AF_INET, static_netmask, &netmask) < 0 ||
|
|
||||||
net_addr_pton(AF_INET, static_gateway, &gw) < 0) {
|
|
||||||
LOG_ERR("Invalid static IP configuration");
|
|
||||||
return -EINVAL;
|
|
||||||
}
|
|
||||||
|
|
||||||
struct net_if_addr *ifaddr =
|
|
||||||
net_if_ipv4_addr_add(iface, &ipaddr, NET_ADDR_MANUAL, 0);
|
|
||||||
if (ifaddr == NULL) {
|
|
||||||
LOG_ERR("Failed to add IPv4 address");
|
|
||||||
return -EIO;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = net_if_ipv4_set_netmask_by_addr(iface, &ipaddr, &netmask);
|
|
||||||
if (rc < 0) {
|
|
||||||
LOG_ERR("Failed to set netmask: %d", rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
net_if_ipv4_set_gw(iface, &gw);
|
|
||||||
|
|
||||||
const char *dns_servers_str[] = {
|
|
||||||
static_dns,
|
|
||||||
NULL, // Terminate the list with NULL
|
|
||||||
};
|
|
||||||
struct sockaddr_in dns_server;
|
|
||||||
dns_server.sin_family = AF_INET;
|
|
||||||
dns_server.sin_port = htons(53);
|
|
||||||
rc = net_addr_pton(AF_INET, static_dns, &dns_server.sin_addr);
|
|
||||||
if (rc < 0) {
|
|
||||||
LOG_ERR("Invalid DNS server address");
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
const struct sockaddr *dns_servers_sa[] = {
|
|
||||||
(struct sockaddr *)&dns_server,
|
|
||||||
NULL, // Terminate the list with NULL
|
|
||||||
};
|
|
||||||
|
|
||||||
struct dns_resolve_context dns_ctx;
|
|
||||||
rc = dns_resolve_init(&dns_ctx, dns_servers_str, dns_servers_sa);
|
|
||||||
if (rc < 0) {
|
|
||||||
LOG_ERR("Failed to add DNS server: %d", rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
|
|
||||||
rc = net_if_up(iface);
|
|
||||||
if (rc < 0) {
|
|
||||||
LOG_ERR("Failed to bring up network interface %s: %d",
|
|
||||||
net_if_get_device(iface)->name,
|
|
||||||
rc);
|
|
||||||
return rc;
|
|
||||||
}
|
|
||||||
LOG_INF("Static IP configuration set");
|
|
||||||
LOG_INF(" IP: %s", static_ip);
|
|
||||||
LOG_INF(" Netmask: %s", static_netmask);
|
|
||||||
LOG_INF(" Gateway: %s", static_gateway);
|
|
||||||
LOG_INF(" DNS: %s", static_dns);
|
|
||||||
}
|
}
|
||||||
return 0;
|
|
||||||
}
|
static int cmd_config_show(const struct shell *shell, size_t argc, char **argv)
|
||||||
|
{
|
||||||
|
ARG_UNUSED(argc);
|
||||||
|
ARG_UNUSED(argv);
|
||||||
|
shell_print(shell, "DHCP: %s", dhcp_enabled ? "ENABLED" : "DISABLED");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int cmd_config_set_dhcp(const struct shell *shell, size_t argc, char **argv)
|
||||||
|
{
|
||||||
|
if (argc != 2) {
|
||||||
|
shell_print(shell, "Usage: config set dhcp <0|1>");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
int val = atoi(argv[1]);
|
||||||
|
if (val != 0 && val != 1) {
|
||||||
|
shell_print(shell, "Value must be 0 (false) or 1 (true)");
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
dhcp_enabled = (val != 0);
|
||||||
|
int rc = settings_save_one("net/dhcp", &dhcp_enabled, sizeof(dhcp_enabled));
|
||||||
|
if (rc < 0) {
|
||||||
|
shell_error(shell, "Failed to save DHCP setting: %d", rc);
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
shell_print(shell, "DHCP set to %s and saved", dhcp_enabled ? "ENABLED" : "DISABLED");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
SHELL_STATIC_SUBCMD_SET_CREATE(sub_config_set,
|
||||||
|
SHELL_CMD(dhcp, NULL, "Set DHCP, 0=disabled, 1=enabled", cmd_config_set_dhcp),
|
||||||
|
SHELL_SUBCMD_SET_END
|
||||||
|
);
|
||||||
|
|
||||||
|
SHELL_STATIC_SUBCMD_SET_CREATE(sub_config,
|
||||||
|
SHELL_CMD(show, NULL, "Show current config", cmd_config_show),
|
||||||
|
SHELL_CMD(set, &sub_config_set, "Change config", NULL),
|
||||||
|
SHELL_SUBCMD_SET_END
|
||||||
|
);
|
||||||
|
|
||||||
|
SHELL_CMD_REGISTER(config, &sub_config, "Configuration settings", NULL);
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue