Skip to content

Arduino library for setting up a network of ESP NOW nodes

License

Notifications You must be signed in to change notification settings

Johboh/EspNowNetwork

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 

Repository files navigation

EspNowNetwork

PlatformIO CI Node ESP-IDF CI Node Arduino IDE Node GitHub release

PlatformIO CI Host ESP-IDF CI Host Arduino IDE Host GitHub release

PlatformIO CI Host driver ESP-IDF CI Host Driver Arduino IDE Host Driver GitHub release

PlatformIO CI Shared ESP-IDF CI Shared Arduino IDE Shared GitHub release

Summary

Arduino (using Arduino IDE or PlatformIO) and ESP-IDF (using Espressif IoT Development Framework or PlatformIO) compatible libraries for setting up a network of ESP-NOW nodes.

Usage/Purpose

The prmary use case for the EspNowNetwork is to run a ESP-NOW network of battery powered nodes with sensors, where the nodes will sleep most of the time and low power consumption is an important factor. Nodes will wake up either due to external interrupt (like a PIR sensor or switch) or perodically based on time. Upon wakeup, they will send their sensors values and go back to sleep. On the receiving side, there is a always powered router board that will receive the sensor values and act on or forward them for consumption somewhere else, like MQTT and/or Home Assistant.

Features

  • Encryption: ESP-NOW have built in encryption, but that relies on that the host adds all peers to be able to decrypt messages. There is a limit on how many peers one can have when using encryption (17). So instead there is an application layer encryption applied using GCM. Each message is unique and valid only once to prevent replay attacks.
  • Generic firmware: For boards that do the same thing (e.g. they have the same hardware), the same firmware can be used for all of them. No unique ID is required to be programmed into each board/node.
  • Over The Air/OTA: A node can be updated Over The Air. The node report their firmware version upon handsake, and the host can send back wifi credentials and an URL where to download the new firmware. The node will download the firmware, flash it and restart.
  • Remote configuration: The host can send configuration or other payload to configure the nodes, like setting wakeup period or similar.

Installation

There are a set if different variants of this library you can use.

For the Node

  • EspNowNetworkNode: This library provide a way to setup ESP-NOW and for sending messages, as well as doing OTA updates when the host indicates that a new firmware version is available.

    • PlatformIO: Add the following to libs_deps:
      Johboh/EspNowNetworkNode@^0.8.1
      
    • ESP-IDF: Add to idf_component.yml next to your main component:
      dependencies:
        johboh/espnownetworknode:
          version: ">=0.8.1"
      
    • Arduino IDE: Search for EspNowNetworkNode by johboh in the library manager.

    See the Arduino or ESP-IDF for full examples. In short (this is not a complete example):

    struct MyApplicationMessage {
      uint8_t id = 0x01;
      double temperature;
    };
    
    EspNowPreferences _esp_now_preferences;
    EspNowCrypt _esp_now_crypt(esp_now_encryption_key, esp_now_encryption_secret);
    EspNowNode _esp_now_node(_esp_now_crypt, _esp_now_preferences, FIRMWARE_VERSION, _on_status, _on_log,
                           arduino_esp_crt_bundle_attach);
    
    void main() {
      _esp_now_preferences.initalizeNVS();
      _esp_now_node.setup();
      _esp_now_node.sendMessage(&message, sizeof(MyApplicationMessage));
      esp_deep_sleep(SLEEP_TIME_US);
    }

For the host

  • EspNowNetworkHostDriver: Use this for your host. This library receives messages from the nodes and forward or handle the received data by handling nodes as Devices. It also provide a way to perform firmware updates by incoperating a Firmware Checker which checks for new firmwares on a HTTP server. It is also possible to implement a custom Firmware Checker to match your HTTP server setup. There is an example of a HTTP server to use for the firmware for the default implementation of the Firmware Checker located here.

    • PlatformIO: Add the following to libs_deps:
      Johboh/EspNowNetworkHostDriver@^0.7.5
      
    • ESP-IDF: Add to idf_component.yml next to your main component:
      dependencies:
        johboh/espnownetworkhostdriver:
          version: ">=0.7.5"
      
    • Arduino IDE: Search for EspNowNetworkHostDriver by johboh in the library manager.

    See the Arduino or ESP-IDF for full examples. In short (this is nota complete example):

    DeviceFootPedal _device_foot_pedal_left(0x543204017648, "Left");
    DeviceFootPedal _device_foot_pedal_right(0x543204016bfc, "Right");
    
    std::vector<std::reference_wrapper<Device>> _devices{_device_foot_pedal_left, _device_foot_pedal_right};
    DeviceManager _device_manager(_devices, []() { return _mqtt_remote.connected(); });
    FirmwareChecker _firmware_checker(firmware_update_base_url, _devices);
    HostDriver _host_driver(_device_manager, wifi_ssid, wifi_password, esp_now_encryption_key esp_now_encryption_secret,
                          [](const std::string message, const std::string sub_path, const bool retain) {
                            _mqtt_remote.publishMessage(_mqtt_remote.clientId() + sub_path, message, retain);
                          });
    
    void setup() {
      _host_driver.setup(_firmware_checker);
    }
    
    void loop() {
      _device_manager.handle();
      _firmware_checker.handle();
    }
  • EspNowNetworkHost: This is just the bare host library, without a Device Manager, Host Driver nor Firmware Checker. I still recommend using the EspNowNetworkHostDriver, but if you want to roll the host fully on your own, this is the library to use.

    • PlatformIO: Add the following to libs_deps:
      Johboh/EspNowNetworkHost@^0.8.1
      
    • ESP-IDF: Add to idf_component.yml next to your main component:
      dependencies:
        johboh/espnownetworkhost:
          version: ">=0.8.1"
      
    • Arduino IDE: Search for EspNowNetworkHost by johboh in the library manager.
  • EspNowNetwork: This is the legacy full library consiting of both the node and the host code (but not the host driver). Not recommended for new projects. Instead, use the induvidual libraries listed above.

Package flow and challenge requests

sequenceDiagram
    participant Host
    participant Node

    Note right of Node: Sleeping...
    Note right of Node: Zzz...

    alt With challenge requests (default required)
        Node->>Host: Challenge Request
        alt Normal response
            Host-->>Node: Challenge Reply (challenge = 42)
            Node->>Host: Application Message (using challenge 42)
            Host-->>Host: Handle application message (verify challenge is 42)
        else Firmware update
            Host-->>Node: Challenge Reply (new firmware available)
            Node->>Node: Download firmware via HTTP
            Node->>Node: Apply firmware and restart
        else Payload reply
            Host-->>Node: Challenge Reply (with payload + challenge = 42)
            Node->>Host: Application Message (using challenge 42)
            Host-->>Host: Handle application message (verify challenge is 42)
        end
    else Wihout challenge request
        Node->>Host: Application Message (no challenge)
        Host-->>Host: Handle application message (no verification)
    end

Loading

If the host allows it, challenge requests can be skipped by calling allowToSkipChallengeVerification and nodes can configure to not send challenge requests by configuring the challenge_requests integer. The host will require challenge request unless explicilty disabled per node.

Normally challenge requests are used to prevent replay attacks, by nodes requesting a unique challenge from the host that the node use in the subsequent message. This challenge is randomized by the host. Upon responding to the challenge request, the host also have the option to indicate that there is a new firmware as well as send any additional payload/configuration. By skipping the challenge request, these two options are not possible. During the challenge request, there is a detection mechanism to detect WiFi channel change as well as change of host, if we prevoulsy had a valid channel and host. If challenge request is disabled, this check will also not happen. However, if the sendMessage function return non positive result, one could assume the the channel/host is invalid and can fall forgetHost() to try finding the host/channel again on next message sending attempt.

Challenge requests involves sending and receiving one additional package. Disabling challenge request will reduce latency and save power consumption, but with a cost in terms of reduced security, error detection and firwmare/payload support.

One could ocacionally send messages with challenge request to check for firmware update, payloads and detect WiFi channel/host changes.

Firmare Update/OTA

Firmare updates works in such a way that when the host received a challenge request from the node, the host checks if there is a new firmware, and if so, return a firmware update reply to the client. This reply contains wifi credentials and a link to the binary to download the new firmware from. The node will then connect to WiFi and downloads the firmware. In future versions, there might be a way to upload the firmware via ESP-NOW, eliminating WiFi, but this will be a much slower process.

If using EspNowNetworkHostDriver, there is an example of a HTTP server to use for the firmware for the default implementation of the Firmware Checker located here.

Deliver payload/configuration

The host can deliver payload to a node, which will be delivered as a response to a challenge request by the node. Use the setPayload to set the payload for a node. The node will receive the payload in the Result returned by the sendMessage function.

For this to work, challenge requests needs to be enabled (enabled by default).

Unix timestamp

The host includes a timestamp in milliseconds in each challange response (in the normal and payload responses, not the firmware update one). The node can access this in the Result returned by the sendMessage function.

For this to work the host need to set the time accordingly. Check out the examples for the host drivers on how to do this.

Examples

Parition table (for the Node (and for the host/host driver if using OTA for the host))

You need to have two app partitions in your parition table to be able to swap between otas. This is an example:

# Name,   Type,  SubType, Offset,          Size, Flags
nvs,      data,      nvs,       ,           16K
otadata,  data,      ota,       ,            8K
phy_init, data,      phy,       ,            4K
coredump, data, coredump,       ,           64K
ota_0,     app,    ota_0,       ,         1500K
ota_1,     app,    ota_1,       ,         1500K
spiffs,   data,   spiffs,       ,          800K

To set partition table, save above in a file called partitions_with_ota.csv. For ESP-IDF, specify to use this one using menuconfig. For platformIO, add the following to your platformio.ini: board_build.partitions = partitions_with_ota.csv

Functionallity verified on the following platforms and frameworks

Newer version most probably work too, but they have not been verified.

Dependencies

  • EspNowNetworkShared
  • Needs C++17 for std::optional.
    • For platformIO in platformio.ini:
      build_unflags=-std=gnu++11 # "Disable" C++11
      build_flags=-std=gnu++17 # "Enable" C++17

About

Arduino library for setting up a network of ESP NOW nodes

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 2

  •  
  •