# Network Split with Host Deep Sleep Example
This example shows how to use two powerful ESP-Hosted features together: **Network Split** and **Host Deep Sleep**. These features allow a host MCU to save power by sleeping while the slave device keeps the network connection alive and only wakes the host when necessary.
## What This Example Does
**Network Split** - Smart traffic routing:
- Host handles important traffic (ports 49152-61439)
- Slave handles background traffic (ports 61440-65535)
- No unnecessary host wake-ups
**Host Deep Sleep** - Ultra-low power mode:
- Host can sleep while slave stays connected to WiFi
- Slave wakes host only for important traffic
- Wake-up can also be triggered by commands or MQTT messages
## Supported Platforms and Transports
### Supported Co-processors
| Co-Processors Supported | ESP32-C5 | ESP32-C6/C61 | ESP32 | ESP32-S2 | ESP32-S3 |
| :---------------------- | :------- | :----------- | :---- | :------- | :------- |
### Supported Host MCUs
| Hosts Supported | ESP32-P4 | ESP32-H2 | Any other MCU hosts |
| :-------------- | :------- | :------- | :------------------ |
### Supported Transport Interfaces
| Transport Interface | SDIO | SPI Full-Duplex | SPI Half-Duplex | UART |
| :------------------ | :--- | :-------------- | :-------------- | :--- |
## Example Hardware Used
This example is designed for the **ESP32-P4-Function-EV-Board** with its built-in ESP32-C6.
**Default setup:**
- **Host**: ESP32-P4 (handles important network traffic)
- **Slave**: ESP32-C6 (maintains WiFi and handles background traffic)
- **Connection**: SDIO (pre-wired on the board)
- **Wake-up wire**: Already connected between GPIO2 (slave) and GPIO6 (host)
All listed items in default setup are customizable to suit specific use cases and requirements.
## Flash the Slave (ESP32-C6)
The slave needs ESP-Hosted firmware with Network Split and Host Power Save enabled.
With below additional configuration, Flash the slave using setup instructions, see: [Slave Example Guide](../../slave/README.md)
#### Slave Side Configuration:
Using `idf.py menuconfig`, Enable:
```
# Minimal slave config
Example Configuration
├── [*] Enable Network Split
└── [*] Allow host to power save
```
**Advanced slave settings** (optional):
```
Example Configuration
└── Network Split Configuration
├── Host Static Port Forwarding
│ ├── TCP dst: 22,80,443,8080,8554
│ └── UDP dst: 53,123
├── Port Ranges
│ ├── Host: 49152–61439
│ └── Slave: 61440–65535
└── Host power save config
├── Allow host to enter deep sleep
├── Slave out: Host wakeup GPIO (2)
└── Host Wakeup GPIO Level (High)
```
Continue to build and flash slave using [Slave Example Guide](../../slave/README.md)
## Flash the Host (ESP32-P4)
The host needs ESP-Hosted firmware with Network Split and Host Power Save configured.
**Essential host configuration:**
1. Set target: `idf.py set-target esp32p4`
2. Network split and Host power save is pre-configured in this example.
(Optionally) customise the configuration using `idf.py menuconfig`
```
Component config
└── ESP-Hosted config
├── [*] Enable Network Split
│ └── Network Split Configuration
│ └── Port Ranges
│ ├── Host: 49152–61439
│ └── Slave: 61440–65535
└── [*] Enable Host Power Save
└── Host Power Save Configuration
[*] Allow host to enter deep sleep.
└── Deep Sleep Configuration
├── Host Wakeup GPIO (6)
└── Host wakeup GPIO active low
```
3. Build and flash: `idf.py build && idf.py -p <HOST_PORT> flash monitor`
## Testing the Example
### Basic Power Save Test
1. Wait for both devices to connect to WiFi
2. Connect to AP using command, `sta_connect <SSID> <Password>`
3. On host console: type `host-power-save`
4. Host goes to sleep, slave stays connected
5. On slave console: type `wake-up-host` to wake the host
### Smart Wake-up Test
1. Put host to sleep using `host-power-save`
2. Send dummy TCP/UDP packet to the device IP in host ports (49152–61439) → host wakes automatically
3. Send dummy TCP/UDP packet to the device IP in slave ports (61440-65535) → slave handles without waking host
##### Sample program to test Network based `Smart Wake-up Test`
<summary>C program to send UDP packet:</br></br>send_udp_pkt.c</summary>
<details>
```c
// SPDX-License-Identifier: Apache-2.0
// Copyright 2015-2025 Espressif Systems (Shanghai) PTE LTD
/* This is just sample program to send sample udp packet */
/*
====================================================================
send_udp_pkt.c
====================================================================
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <errno.h>
int main(int argc, char *argv[]) {
if (argc != 3) {
fprintf(stderr, "Usage: %s <IP> <PORT>\n", argv[0]);
return EXIT_FAILURE;
}
const char *ip = argv[1];
int port = atoi(argv[2]);
const char *msg = "Hello, UDP!";
int sock = socket(AF_INET, SOCK_DGRAM, 0);
if (sock < 0) {
perror("socket");
return EXIT_FAILURE;
}
// Non-blocking mode
int flags = fcntl(sock, F_GETFL, 0);
fcntl(sock, F_SETFL, flags | O_NONBLOCK);
struct sockaddr_in addr = {
.sin_family = AF_INET,
.sin_port = htons(port),
};
inet_pton(AF_INET, ip, &addr.sin_addr);
ssize_t sent = sendto(sock, msg, strlen(msg), 0,
(struct sockaddr *)&addr, sizeof(addr));
if (sent < 0) {
if (errno == EWOULDBLOCK || errno == EAGAIN) {
fprintf(stderr, "sendto would block, try again later\n");
} else {
perror("sendto");
}
} else {
printf("Sent %zd bytes to %s:%d\n", sent, ip, port);
}
close(sock);
return EXIT_SUCCESS;
}
```
</details>
<summary>Shell script to trigger wake-up: </br></br>send_udp_pkt.sh</summary>
<details>
```bash
#!/bin/bash
# Simple script to build and run send_udp_pkt.c
#====================================================================
# send_udp_pkt.sh
#====================================================================
# Check if IP address is provided
if [ $# -eq 0 ]; then
echo "❌ Error: IP address is required!"
echo "Usage: $0 <ip_address> [port]"
echo "Example: $0 192.168.1.100 60000"
exit 1
fi
IP_ADDRESS="$1"
DEFAULT_PORT="60000"
# Check if port is provided as second argument
if [ $# -ge 2 ]; then
PORT="$2"
else
PORT="$DEFAULT_PORT"
echo "No port specified, using default port: $DEFAULT_PORT"
fi
echo "===== Network Split Host Wakeup with UDP Packet Tool ====="
echo "Target: $IP_ADDRESS:$PORT"
echo "Building and running network wakeup utility..."
# Compile the program
gcc -o send_udp_pkt send_udp_pkt.c -pthread
# Check if compilation succeeded
if [ $? -ne 0 ]; then
echo "❌ Compilation failed!"
exit 1
fi
echo "✅ Build successful!"
echo "Starting network wakeup utility..."
echo "-------------------------------------------"
# Run the program with IP address and port
./send_udp_pkt $IP_ADDRESS $PORT
# Capture result
RESULT=$?
if [ $RESULT -eq 0 ]; then
echo "-------------------------------------------"
echo "✅ Network wakeup packet sent successfully to $IP_ADDRESS:$PORT!"
else
echo "-------------------------------------------"
echo "❌ Network wakeup failed with code: $RESULT"
fi
exit $RESULT
```
</details>
Create both files in some temporary directory and run:
```bash
bash send_udp_pkt.sh <device_ip_address> 62000 # Send packet to slave port --> ❌ No host wake up
bash send_udp_pkt.sh <device_ip_address> 123 # Send packet to host reserved udp port ==> ✅ host wake up
```
> [!TIP]
>
> You can design your packet routing & filtering as per application application use-case and deploy in slave firmware.
> Refer to the https://github.com/espressif/esp-hosted-mcu/blob/663d6631af6e7a6735755e2247ab60363fda87c8/slave/main/lwip_filter.c#L349
### Performance Test
1. Run iPerf server: `iperf -s -p 5001`
2. From another device: `iperf -c <device_ip> -p 5001`
3. Watch logs to see which device handles the traffic
For more details on iperf tests, refer [iperf test](README_iperf.md)
## Customizing for Other Hardware
#### Different Transport (e.g., SPI instead of SDIO)
**Slave side:**
```
Example Configuration
└── Bus Config in between Host and Co-processor
└── Transport layer
└── (X) SPI Full-duplex
```
**Host side:**
```
Component config
└── ESP-Hosted config
└── Transport layer
└── (X) SPI Full-duplex
```
Configure the specific GPIO pins for the chosen transport bus and Host wake up GPIO pin
#### Different MCU Combination
1. Change `idf.py set-target` to your MCU
2. Update GPIO pins in menuconfig to match your wiring
3. Make sure wake-up GPIO on host supports deep sleep wake-up
## Troubleshooting
**Host won't wake up:**
- Check GPIO wire connection between slave GPIO2 and host GPIO6
- Verify host GPIO6 supports RTC wake-up
- Check GPIO level settings match on both sides
**WiFi connection fails:**
- Double-check WiFi credentials in both devices
- Make sure both devices use the same WiFi settings
**Traffic not splitting correctly:**
- Check port range settings in slave menuconfig
- Monitor logs to see packet routing decisions
- Verify Network Split is enabled on slave
## References
- [Network Split Documentation](../../docs/feature_network_split.md)
- [Host Power Save Documentation](../../docs/feature_host_power_save.md)
- [ESP32-P4-Function-EV-Board Guide](../../docs/esp32_p4_function_ev_board.md)
- [Slave Example Guide](../../slave/README.md)
- [Main ESP-Hosted Documentation](../../README.md)
To create a project from this example, run:
idf.py create-project-from-example "espressif/esp_hosted=2.6.2:host_network_split__power_save"