+++
title = "WiLE: A WiFi-Based Low Energy Mesh Network"
date = 2022-12-01
tags = ["WiFi", "BLE", "Mesh Network", "ESP32"]
+++

Home automation, a fun gimmick or very useful… Doesn't matter what your opinion is on this subject, but one cannot deny that designing smart appliances yourself is a fun activity. This is the reason that some of my lights are controlled by a small ESP32-based board, which in turn are controlled by ArtNet over WiFi (ArtNet being DMX with some extensions over UDP). 

This works pretty well and since it uses UDP, it can be controlled by any ArtNet controller somewhere on the network. This works as long as the boards are constantly connected to a power source. Running from a battery is less ideal since maintaining a WiFi connection on an ESP32 (and pretty much, if not all, other MCUs as well) is quite a power-hungry task.

Other types of networks do exist as well: think of ESP mesh, WiFi mesh, BLE mesh (all supported by the ESP32), or Zigbee and others. But what's the fun in that…? Since this project is only going to be used in my own house, it seemed more fun to design a custom mesh network from the ground up!

Of course, this wasn't my idea from the start… I just thought "Let's use BLE mesh or plain WiFi and it's done." Until I read an interesting paper: [Wi-LE: Can WiFi Replace Bluetooth?](https://doi.org/10.1145/3365609.3365853) by Abedi et al. It talks about using the 802.11 beacon frames for information exchange (commonly known as beacon stuffing). They found that the transmission of one beacon frame takes much less energy (and way less time) than first connecting to a WiFi network and then sending out a UDP packet. They also found that the energy per data-containing packet is in the same order of magnitude as a BLE packet, hence calling this method WiLE or WiFi Low Energy.

### A bit about BLE

Before diving into my ideas, let’s first take a short look at Bluetooth Low Energy.

In BLE, each device periodically sends out beacons and periodically listens for beacons (or listens continuously, which consumes quite a bit of energy). After some waiting, a device sends out three beacon frames, each on a different advertisement channel, with a small random time between them, and then waits until the next beacon period. 

If another device is listening on that specific advertisement channel at the right time, it receives the beacon frame and delivers the information to the application. Since there's quite a bit of chance involved in receiving a beacon frame, the receiver will probably be on for most of the time. This means that devices needing to receive data must stay awake continuously — which isn't ideal for low-power devices like light switches.

This made me wonder: can I create a network that allows devices to sleep between beacon periods *and* still receive data? This is where WiLE comes into play. On an ESP32 using ESP-IDF, we can control both beacon transmit times and the receiver state via `esp_wifi_80211_tx`, allowing us to synchronize devices and exchange data using custom-crafted beacon frames.

Bluetooth Mesh builds on BLE and uses its beacons (GAP) to exchange data (though it can use GATT too). It routes messages by flooding and encrypts with two layers: a network key (to identify valid members of the mesh) and an application key (for application-specific data, e.g., turning on a light). Devices are provisioned into the network using a device key.

### The main idea

![Figure 1, basic WiLE mesh beacon timing](/images/blog/wile/wile_timing0.png "Figure 1, basic WiLE mesh beacon timing")

The basic principle is a time-synchronized mesh where devices send application-specific beacons within a short interval. Outside of this beacon interval, devices can sleep to conserve energy.

A full cycle might look like this:

- **Wakeup period**: A device wakes, turns on WiFi, and starts listening.
- **Active period**: Synchronized devices each send their beacon (at a random moment to avoid collisions).
- **Inactive period**: WiFi is disabled and the device sleeps until the next cycle.

If all devices share the same cycle period **T**, and the same start/end times **t1** and **t2**, they can remain synchronized.

Extensions are easy. For instance, low-power devices (LPDs) don’t need to wake every cycle. Instead, each LPD is assigned a “friend device” that stores messages for it. When the LPD wakes (e.g., due to a button press), it waits for the next cycle, sends a beacon, and stays active slightly longer to receive P2P data from its friend.

Messages are encrypted similarly to BLE mesh: network + application keys. P2P (e.g., provisioning) can use device-specific keys.

### Time synchronization

Until now, we assumed all devices are synchronized. Unfortunately, this doesn’t happen magically. NTP is not feasible here — no internet, and only one beacon per cycle.

Luckily, I found [this 2004 technical report](http://disc.ece.illinois.edu/publications/clocksync-tech.pdf): **A Distributed Self-Stabilizing Time Synchronization Protocol for Multi-hop Wireless Networks** by So & Vaidya. It discusses MTSP, a protocol that adjusts local clocks based on beacon reception.

Each device keeps a microsecond-resolution timer (doesn't mean microsecond *precision*). Its state contains:

- A parent ID
- TTL counter for parent and children

Each beacon includes:

- Time of transmission
- Source’s **t1**
- Source’s parent ID

If a device receives a beacon with a *newer* time, it sets that device as parent and adjusts its own time at the end of the cycle. If it receives a beacon from a *child* with an *older* timestamp, it resets the TTL counter for that child.

If TTL expires (e.g., after a few missed cycles), the device resets to unsynchronized state.

### Basic frame layout

![Figure 2, frame layout of normal frames](/images/blog/wile/nframe_layout.png "Figure 2, frame layout of normal frames")

Figure 2 shows the layout of normal 802.11 management frames used in WiLE. Each includes:

- Standard MAC header and beacon format
- Custom data in vendor-specific field (ID 211)
- A "magic" string + frame type ("N" for normal)
- Time sync data: timestamp, **t1**, and parent ID
- Application data payload

The SSID length is set to 0 so nodes remain invisible. These beacons won’t interfere with surrounding networks, and connection attempts will fail as WiLE ignores packets without the magic string.

---

**_Work in progress…_**
