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:
Eduard Iten 2025-07-20 08:30:33 +02:00
parent cbbd5f5fea
commit 6e669cfc4e
6 changed files with 429 additions and 293 deletions

View File

@ -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>`.

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
View File

@ -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);