├── .github └── FUNDING.yml ├── .gitignore ├── .travis.yml ├── .vscode └── extensions.json ├── README.md ├── include └── README ├── lib └── README ├── platformio.ini ├── src └── main.cpp └── test └── README /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: [atomic14] 4 | ko_fi: atomic14 5 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .pio 2 | .vscode/.browse.c_cpp.db* 3 | .vscode/c_cpp_properties.json 4 | .vscode/launch.json 5 | .vscode/ipch 6 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # Continuous Integration (CI) is the practice, in software 2 | # engineering, of merging all developer working copies with a shared mainline 3 | # several times a day < https://docs.platformio.org/page/ci/index.html > 4 | # 5 | # Documentation: 6 | # 7 | # * Travis CI Embedded Builds with PlatformIO 8 | # < https://docs.travis-ci.com/user/integration/platformio/ > 9 | # 10 | # * PlatformIO integration with Travis CI 11 | # < https://docs.platformio.org/page/ci/travis.html > 12 | # 13 | # * User Guide for `platformio ci` command 14 | # < https://docs.platformio.org/page/userguide/cmd_ci.html > 15 | # 16 | # 17 | # Please choose one of the following templates (proposed below) and uncomment 18 | # it (remove "# " before each line) or use own configuration according to the 19 | # Travis CI documentation (see above). 20 | # 21 | 22 | 23 | # 24 | # Template #1: General project. Test it using existing `platformio.ini`. 25 | # 26 | 27 | # language: python 28 | # python: 29 | # - "2.7" 30 | # 31 | # sudo: false 32 | # cache: 33 | # directories: 34 | # - "~/.platformio" 35 | # 36 | # install: 37 | # - pip install -U platformio 38 | # - platformio update 39 | # 40 | # script: 41 | # - platformio run 42 | 43 | 44 | # 45 | # Template #2: The project is intended to be used as a library with examples. 46 | # 47 | 48 | # language: python 49 | # python: 50 | # - "2.7" 51 | # 52 | # sudo: false 53 | # cache: 54 | # directories: 55 | # - "~/.platformio" 56 | # 57 | # env: 58 | # - PLATFORMIO_CI_SRC=path/to/test/file.c 59 | # - PLATFORMIO_CI_SRC=examples/file.ino 60 | # - PLATFORMIO_CI_SRC=path/to/test/directory 61 | # 62 | # install: 63 | # - pip install -U platformio 64 | # - platformio update 65 | # 66 | # script: 67 | # - platformio ci --lib="." --board=ID_1 --board=ID_2 --board=ID_N 68 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": [ 5 | "platformio.platformio-ide" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | This project demonstrates how to use ESP-NOW to send messages to other ESP devices without using WiFi. 4 | 5 | You can find an overview of ESP-NOW [here](https://www.espressif.com/en/products/software/esp-now/overview). 6 | 7 | And detailed documentation [here](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/network/esp_now.html). 8 | 9 | [![Demo Video](https://img.youtube.com/vi/w4R9VoY96h8/0.jpg)](https://www.youtube.com/watch?v=w4R9VoY96h8) 10 | 11 | The code has been written using platform.ide, but you it will work equally well in the Arduino IDE. 12 | 13 | # Setup 14 | 15 | For the broadcast example just push the sketch to a couple of ESP32 devices. You should see that pushing the "boot" button on one of the devices turns the LED on and off on both devices. 16 | 17 | To set up peer-to-peer communication make a note of the MAC address of one of the devices and use that instead of the broadcast address. 18 | -------------------------------------------------------------------------------- /include/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project header files. 3 | 4 | A header file is a file containing C declarations and macro definitions 5 | to be shared between several project source files. You request the use of a 6 | header file in your project source file (C, C++, etc) located in `src` folder 7 | by including it, with the C preprocessing directive `#include'. 8 | 9 | ```src/main.c 10 | 11 | #include "header.h" 12 | 13 | int main (void) 14 | { 15 | ... 16 | } 17 | ``` 18 | 19 | Including a header file produces the same results as copying the header file 20 | into each source file that needs it. Such copying would be time-consuming 21 | and error-prone. With a header file, the related declarations appear 22 | in only one place. If they need to be changed, they can be changed in one 23 | place, and programs that include the header file will automatically use the 24 | new version when next recompiled. The header file eliminates the labor of 25 | finding and changing all the copies as well as the risk that a failure to 26 | find one copy will result in inconsistencies within a program. 27 | 28 | In C, the usual convention is to give header files names that end with `.h'. 29 | It is most portable to use only letters, digits, dashes, and underscores in 30 | header file names, and at most one dot. 31 | 32 | Read more about using header files in official GCC documentation: 33 | 34 | * Include Syntax 35 | * Include Operation 36 | * Once-Only Headers 37 | * Computed Includes 38 | 39 | https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html 40 | -------------------------------------------------------------------------------- /lib/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for project specific (private) libraries. 3 | PlatformIO will compile them to static libraries and link into executable file. 4 | 5 | The source code of each library should be placed in a an own separate directory 6 | ("lib/your_library_name/[here are source files]"). 7 | 8 | For example, see a structure of the following two libraries `Foo` and `Bar`: 9 | 10 | |--lib 11 | | | 12 | | |--Bar 13 | | | |--docs 14 | | | |--examples 15 | | | |--src 16 | | | |- Bar.c 17 | | | |- Bar.h 18 | | | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html 19 | | | 20 | | |--Foo 21 | | | |- Foo.c 22 | | | |- Foo.h 23 | | | 24 | | |- README --> THIS FILE 25 | | 26 | |- platformio.ini 27 | |--src 28 | |- main.c 29 | 30 | and a contents of `src/main.c`: 31 | ``` 32 | #include 33 | #include 34 | 35 | int main (void) 36 | { 37 | ... 38 | } 39 | 40 | ``` 41 | 42 | PlatformIO Library Dependency Finder will find automatically dependent 43 | libraries scanning project source files. 44 | 45 | More information about PlatformIO Library Dependency Finder 46 | - https://docs.platformio.org/page/librarymanager/ldf.html 47 | -------------------------------------------------------------------------------- /platformio.ini: -------------------------------------------------------------------------------- 1 | ; PlatformIO Project Configuration File 2 | ; 3 | ; Build options: build flags, source filter 4 | ; Upload options: custom upload port, speed and extra flags 5 | ; Library options: dependencies, extra library storages 6 | ; Advanced options: extra scripting 7 | ; 8 | ; Please visit documentation for the other options and examples 9 | ; https://docs.platformio.org/page/projectconf.html 10 | 11 | [env:esp32dev] 12 | platform = espressif32 13 | board = esp32dev 14 | framework = arduino 15 | -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | bool buttonDown = false; 5 | bool ledOn = false; 6 | 7 | void formatMacAddress(const uint8_t *macAddr, char *buffer, int maxLength) 8 | { 9 | snprintf(buffer, maxLength, "%02x:%02x:%02x:%02x:%02x:%02x", macAddr[0], macAddr[1], macAddr[2], macAddr[3], macAddr[4], macAddr[5]); 10 | } 11 | 12 | void receiveCallback(const uint8_t *macAddr, const uint8_t *data, int dataLen) 13 | { 14 | // only allow a maximum of 250 characters in the message + a null terminating byte 15 | char buffer[ESP_NOW_MAX_DATA_LEN + 1]; 16 | int msgLen = min(ESP_NOW_MAX_DATA_LEN, dataLen); 17 | strncpy(buffer, (const char *)data, msgLen); 18 | // make sure we are null terminated 19 | buffer[msgLen] = 0; 20 | // format the mac address 21 | char macStr[18]; 22 | formatMacAddress(macAddr, macStr, 18); 23 | // debug log the message to the serial port 24 | Serial.printf("Received message from: %s - %s\n", macStr, buffer); 25 | // what are our instructions 26 | if (strcmp("on", buffer) == 0) 27 | { 28 | ledOn = true; 29 | } 30 | else 31 | { 32 | ledOn = false; 33 | } 34 | digitalWrite(2, ledOn); 35 | } 36 | 37 | // callback when data is sent 38 | void sentCallback(const uint8_t *macAddr, esp_now_send_status_t status) 39 | { 40 | char macStr[18]; 41 | formatMacAddress(macAddr, macStr, 18); 42 | Serial.print("Last Packet Sent to: "); 43 | Serial.println(macStr); 44 | Serial.print("Last Packet Send Status: "); 45 | Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail"); 46 | } 47 | 48 | void broadcast(const String &message) 49 | { 50 | // this will broadcast a message to everyone in range 51 | uint8_t broadcastAddress[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 52 | esp_now_peer_info_t peerInfo = {}; 53 | memcpy(&peerInfo.peer_addr, broadcastAddress, 6); 54 | if (!esp_now_is_peer_exist(broadcastAddress)) 55 | { 56 | esp_now_add_peer(&peerInfo); 57 | } 58 | esp_err_t result = esp_now_send(broadcastAddress, (const uint8_t *)message.c_str(), message.length()); 59 | // and this will send a message to a specific device 60 | /*uint8_t peerAddress[] = {0x3C, 0x71, 0xBF, 0x47, 0xA5, 0xC0}; 61 | esp_now_peer_info_t peerInfo = {}; 62 | memcpy(&peerInfo.peer_addr, peerAddress, 6); 63 | if (!esp_now_is_peer_exist(peerAddress)) 64 | { 65 | esp_now_add_peer(&peerInfo); 66 | } 67 | esp_err_t result = esp_now_send(peerAddress, (const uint8_t *)message.c_str(), message.length());*/ 68 | if (result == ESP_OK) 69 | { 70 | Serial.println("Broadcast message success"); 71 | } 72 | else if (result == ESP_ERR_ESPNOW_NOT_INIT) 73 | { 74 | Serial.println("ESPNOW not Init."); 75 | } 76 | else if (result == ESP_ERR_ESPNOW_ARG) 77 | { 78 | Serial.println("Invalid Argument"); 79 | } 80 | else if (result == ESP_ERR_ESPNOW_INTERNAL) 81 | { 82 | Serial.println("Internal Error"); 83 | } 84 | else if (result == ESP_ERR_ESPNOW_NO_MEM) 85 | { 86 | Serial.println("ESP_ERR_ESPNOW_NO_MEM"); 87 | } 88 | else if (result == ESP_ERR_ESPNOW_NOT_FOUND) 89 | { 90 | Serial.println("Peer not found."); 91 | } 92 | else 93 | { 94 | Serial.println("Unknown error"); 95 | } 96 | } 97 | 98 | void setup() 99 | { 100 | Serial.begin(115200); 101 | delay(1000); 102 | //Set device in STA mode to begin with 103 | WiFi.mode(WIFI_STA); 104 | Serial.println("ESPNow Example"); 105 | // Output my MAC address - useful for later 106 | Serial.print("My MAC Address is: "); 107 | Serial.println(WiFi.macAddress()); 108 | // shut down wifi 109 | WiFi.disconnect(); 110 | // startup ESP Now 111 | if (esp_now_init() == ESP_OK) 112 | { 113 | Serial.println("ESPNow Init Success"); 114 | esp_now_register_recv_cb(receiveCallback); 115 | esp_now_register_send_cb(sentCallback); 116 | } 117 | else 118 | { 119 | Serial.println("ESPNow Init Failed"); 120 | delay(3000); 121 | ESP.restart(); 122 | } 123 | // use the built in button 124 | pinMode(0, INPUT_PULLUP); 125 | pinMode(2, OUTPUT); 126 | } 127 | 128 | void loop() 129 | { 130 | if (digitalRead(0)) 131 | { 132 | // detect the transition from low to high 133 | if (!buttonDown) 134 | { 135 | buttonDown = true; 136 | // toggle the LED state 137 | ledOn = !ledOn; 138 | digitalWrite(2, ledOn); 139 | // send a message to everyone else 140 | if (ledOn) 141 | { 142 | broadcast("on"); 143 | } 144 | else 145 | { 146 | broadcast("off"); 147 | } 148 | } 149 | // delay to avoid bouncing 150 | delay(500); 151 | } 152 | else 153 | { 154 | // reset the button state 155 | buttonDown = false; 156 | } 157 | } -------------------------------------------------------------------------------- /test/README: -------------------------------------------------------------------------------- 1 | 2 | This directory is intended for PIO Unit Testing and project tests. 3 | 4 | Unit Testing is a software testing method by which individual units of 5 | source code, sets of one or more MCU program modules together with associated 6 | control data, usage procedures, and operating procedures, are tested to 7 | determine whether they are fit for use. Unit testing finds problems early 8 | in the development cycle. 9 | 10 | More information about PIO Unit Testing: 11 | - https://docs.platformio.org/page/plus/unit-testing.html 12 | --------------------------------------------------------------------------------