├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── examples ├── SensorClientDigitalInput │ └── SensorClientDigitalInput.ino ├── SensorServer │ └── SensorServer.ino ├── SimpleEspNowConnectionClient │ └── SimpleEspNowConnectionClient.ino └── SimpleEspNowConnectionServer │ └── SimpleEspNowConnectionServer.ino ├── keywords.txt ├── library.json ├── library.properties └── src ├── SimpleEspNowConnection.cpp └── SimpleEspNowConnection.h /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | *.d 3 | 4 | # Compiled Object files 5 | *.slo 6 | *.lo 7 | *.o 8 | *.obj 9 | 10 | # Precompiled Headers 11 | *.gch 12 | *.pch 13 | 14 | # Compiled Dynamic libraries 15 | *.so 16 | *.dylib 17 | *.dll 18 | 19 | # Fortran module files 20 | *.mod 21 | *.smod 22 | 23 | # Compiled Static libraries 24 | *.lai 25 | *.la 26 | *.a 27 | *.lib 28 | 29 | # Executables 30 | *.exe 31 | *.out 32 | *.app 33 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Erich O. Pintar 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # EspNow Library for communication and device pairing 2 | This library provides ESPNow communication with pairing functionality. 3 | It does not use the inbuild pairing functionality provided by the ESPNow interface. 4 | 5 | ## Example 6 | 7 | The library comes with a number of example sketches. See File > Examples > SimpleEspNowConnection within the Arduino application. 8 | 9 | 10 | ## Limitation 11 | 12 | - Encryption/Decryption of payload is not implemented yet. 13 | 14 | 15 | 16 | ## Features 17 | 18 | - In theory, infinite number of clients can be used to communicate with one server 19 | - Maximum number of bytes per message can extend EspNow linitation of 250 bytes. 20 | 21 | 22 | ## Licence 23 | 24 | This code is released under the MIT License. 25 | -------------------------------------------------------------------------------- /examples/SensorClientDigitalInput/SensorClientDigitalInput.ino: -------------------------------------------------------------------------------- 1 | /* 2 | SensorClientDigitalInput 3 | 4 | Basic EspNowConnection Client implementation for a sensor 5 | 6 | HOWTO Hardware Setup: 7 | - Mount two jumper/pushbuttons, one between GPIO13 and ground and one between 8 | GPIO12 and ground 9 | - Connect GPIO16 with RST 10 | - Take a look on "https://github.com/saghonfly/SimpleEspNowConnection/wiki/Hardware-Setup-'SensorClientDigitalInput'-Example" 11 | 12 | 13 | HOWTO Arduino IDE: 14 | - Prepare two ESP8266 or ESP32 based devices (eg. WeMos) or mix an ESP8266 and an ESP32 device 15 | - Start two separate instances of Arduino IDE and load 16 | on the first one the 'SensorServer.ino' and 17 | on the second one the 'SensorClientDigitalInput.ino' sketch and upload 18 | these to the two ESP devices. 19 | - Start the 'Serial Monitor' in both instances and set baud rate to 9600 20 | - Type 'startpair' into the edit box of the SensorServer. Hold the pairing button on the SensorClientDigitalInput device and reset the device 21 | - After server and client are paired, you can change the sleep time of the client in the server 22 | by typing 'settimeout ' into the serial terminal. 23 | This will than be send next time when sensor is up. 24 | 25 | - You can use multiple clients which can be connected to one server 26 | 27 | Created 11 Mai 2020 28 | By Erich O. Pintar 29 | Modified 06 Oct 2020 30 | By Erich O. Pintar 31 | 32 | https://github.com/saghonfly/SimpleEspNowConnection 33 | 34 | */ 35 | #include "SimpleEspNowConnection.h" 36 | 37 | #include 38 | 39 | SimpleEspNowConnection simpleEspConnection(SimpleEspNowRole::CLIENT); 40 | 41 | int pairingButton = 13; 42 | //int pairingButton = 27; 43 | int sensorButton = 12; 44 | bool pairingMode = false; 45 | 46 | String inputString; 47 | uint8_t serverAddress[6]; 48 | uint8_t timeout = 10; 49 | 50 | void OnSendError(uint8_t* ad) 51 | { 52 | Serial.println("Sending to '"+simpleEspConnection.macToStr(ad)+"' was not possible!"); 53 | } 54 | 55 | void OnMessage(uint8_t* ad, const uint8_t* message, size_t len) 56 | { 57 | Serial.printf("MESSAGE from server:%s\n", (char *)message); 58 | 59 | if(String((char *)message).substring(0,8) == "timeout:") 60 | timeout = atoi( String((char *)message).substring(8).c_str() ); 61 | 62 | writeConfig(); 63 | Serial.println("New timeout written to configuration"); 64 | } 65 | 66 | void OnNewGatewayAddress(uint8_t *ga, String ad) 67 | { 68 | memcpy(serverAddress, ga, 6); 69 | 70 | simpleEspConnection.setServerMac(ga); 71 | 72 | Serial.println("Pairing mode finished..."); 73 | 74 | pairingMode = false; 75 | 76 | writeConfig(); 77 | } 78 | 79 | bool readConfig() 80 | { 81 | for(int i=0;i<6;i++) 82 | { 83 | serverAddress[i] = EEPROM.read(i); 84 | } 85 | 86 | timeout = EEPROM.read(6); 87 | return true; 88 | } 89 | 90 | bool writeConfig() 91 | { 92 | Serial.printf("Write %02X%02X%02X%02X%02X%02X to EEPROM\n", serverAddress[0], 93 | serverAddress[1], 94 | serverAddress[2], 95 | serverAddress[3], 96 | serverAddress[4], 97 | serverAddress[5]); 98 | 99 | for(int i=0;i<6;i++) 100 | { 101 | EEPROM.write(i,serverAddress[i]); 102 | } 103 | EEPROM.write(6,timeout); 104 | EEPROM.commit(); 105 | 106 | return true; 107 | } 108 | 109 | void setup() 110 | { 111 | Serial.begin(9600); 112 | Serial.println(); 113 | 114 | EEPROM.begin(13); 115 | 116 | simpleEspConnection.begin(); 117 | simpleEspConnection.setPairingBlinkPort(2); 118 | simpleEspConnection.onNewGatewayAddress(&OnNewGatewayAddress); 119 | simpleEspConnection.onMessage(&OnMessage); 120 | simpleEspConnection.onSendError(&OnSendError); 121 | 122 | // make the pairing pin to input: 123 | pinMode(pairingButton, INPUT_PULLUP); 124 | 125 | // Check if button is pressed when started 126 | int pairingButtonState = digitalRead(pairingButton); 127 | 128 | // start pairing if button is pressed 129 | if(pairingButtonState == 0) 130 | { 131 | Serial.println("Pairing mode ongoing..."); 132 | 133 | pairingMode = true; 134 | simpleEspConnection.startPairing(); 135 | } 136 | else 137 | { 138 | if(!readConfig()) 139 | { 140 | Serial.println("!!! Server address not valid. Please pair first !!!"); 141 | return; 142 | } 143 | 144 | if(!simpleEspConnection.setServerMac(serverAddress)) // set the server address which is stored in EEPROM 145 | { 146 | Serial.println("!!! Server address not valid. Please pair first !!!"); 147 | return; 148 | } 149 | 150 | // make the sensor pin to input: 151 | pinMode(sensorButton, INPUT_PULLUP); 152 | 153 | String sensorButtonState = digitalRead(sensorButton) == 0 ? "SENSOR:PRESSED" : "SENSOR:UNPRESSED"; 154 | 155 | simpleEspConnection.sendMessage((char *)sensorButtonState.c_str()); 156 | } 157 | } 158 | 159 | void loop() 160 | { 161 | simpleEspConnection.loop(); 162 | 163 | if(pairingMode) // do not go to sleep if pairing mode is ongoing 164 | return; 165 | 166 | if(millis() < 100) // wait 100 millisecond for message from server otherwise go to sleep 167 | return; 168 | 169 | if(!simpleEspConnection.isSendBufferEmpty()) 170 | return; 171 | 172 | Serial.printf("Going to sleep...I was up for %i ms...will come back in %d seconds\n", millis(), timeout); 173 | 174 | #if defined(ESP32) 175 | esp_sleep_enable_timer_wakeup(timeout * 1000000); 176 | esp_deep_sleep_start(); 177 | #else 178 | ESP.deepSleep(timeout * 1000000, RF_NO_CAL); // deep sleep for 10 seconds 179 | delay(100); 180 | #endif 181 | } 182 | -------------------------------------------------------------------------------- /examples/SensorServer/SensorServer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | SensorServer 3 | 4 | Basic EspNowConnection Server implementation for a sensor server (gateway) 5 | 6 | HOWTO Arduino IDE: 7 | - Prepare two ESP8266 or ESP32 based devices (eg. WeMos) or mix an ESP8266 and an ESP32 device 8 | - Start two separate instances of Arduino IDE and load 9 | on the first one the 'SensorServer.ino' and 10 | on the second one the 'SensorClientDigitalInput.ino' sketch and upload 11 | these to the two ESP devices. 12 | - Start the 'Serial Monitor' in both instances and set baud rate to 9600 13 | - Type 'startpair' into the edit box of the SensorServer. Hold the pairing button on the SensorClientDigitalInput device and reset the device 14 | - After server and client are paired, you can change the sleep time of the client in the server 15 | by typing 'settimeout ' into the serial terminal. 16 | This will than be send next time when sensor is up. 17 | 18 | - You can use multiple clients which can be connected to one server 19 | 20 | Created 11 Mai 2020 21 | By Erich O. Pintar 22 | Modified 06 Oct 2020 23 | By Erich O. Pintar 24 | 25 | https://github.com/saghonfly/SimpleEspNowConnection 26 | 27 | */ 28 | 29 | #include "SimpleEspNowConnection.h" 30 | 31 | static SimpleEspNowConnection simpleEspConnection(SimpleEspNowRole::SERVER); 32 | String inputString, clientAddress; 33 | String newTimeout = ""; 34 | 35 | void OnSendError(uint8_t* ad) 36 | { 37 | Serial.println("Sending to '"+simpleEspConnection.macToStr(ad)+"' was not possible!"); 38 | } 39 | 40 | void OnMessage(uint8_t* ad, const uint8_t* message, size_t len) 41 | { 42 | Serial.println("Client '"+simpleEspConnection.macToStr(ad)+"' has sent me '"+String((char *)message)+"'"); 43 | } 44 | 45 | void OnPaired(uint8_t *ga, String ad) 46 | { 47 | Serial.println("EspNowConnection : Client '"+ad+"' paired! "); 48 | 49 | simpleEspConnection.endPairing(); 50 | } 51 | 52 | void OnConnected(uint8_t *ga, String ad) 53 | { 54 | if(newTimeout != "") 55 | { 56 | simpleEspConnection.sendMessage((char *)(String("timeout:"+newTimeout).c_str()), ad ); 57 | newTimeout = ""; 58 | } 59 | } 60 | 61 | void setup() 62 | { 63 | Serial.begin(9600); 64 | Serial.println(); 65 | // clientAddress = "ECFABC0CE7A2"; // Test if you know the client 66 | 67 | simpleEspConnection.begin(); 68 | // simpleEspConnection.setPairingBlinkPort(2); 69 | simpleEspConnection.onMessage(&OnMessage); 70 | simpleEspConnection.onPaired(&OnPaired); 71 | simpleEspConnection.onSendError(&OnSendError); 72 | simpleEspConnection.onConnected(&OnConnected); 73 | } 74 | 75 | void loop() 76 | { 77 | simpleEspConnection.loop(); 78 | 79 | while (Serial.available()) 80 | { 81 | char inChar = (char)Serial.read(); 82 | if (inChar == '\n') 83 | { 84 | Serial.println(inputString); 85 | 86 | if(inputString == "startpair") 87 | { 88 | Serial.println("Pairing started..."); 89 | simpleEspConnection.startPairing(120); 90 | } 91 | else if(inputString.substring(0,11) == "settimeout ") 92 | { 93 | Serial.println("Will set new timeout of client next time when the device goes up..."); 94 | newTimeout = atoi(inputString.substring(11).c_str()); 95 | } 96 | 97 | inputString = ""; 98 | } 99 | else 100 | { 101 | inputString += inChar; 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /examples/SimpleEspNowConnectionClient/SimpleEspNowConnectionClient.ino: -------------------------------------------------------------------------------- 1 | /* 2 | SimpleEspNowConnectionClient 3 | 4 | Basic EspNowConnection Client implementation 5 | 6 | HOWTO Arduino IDE: 7 | - Prepare two ESP8266 or ESP32 based devices (eg. WeMos) 8 | - Start two separate instances of Arduino IDE and load 9 | on the first one the 'SimpleEspNowConnectionServer.ino' and 10 | on the second one the 'SimpleEspNowConnectionClient.ino' sketch and upload 11 | these to the two ESP devices. 12 | - Start the 'Serial Monitor' in both instances and set baud rate to 115200 13 | - Type 'startpair' into the edit box of both 'Serial Monitors' and hit Enter key (or press 'Send' button) 14 | - After devices are paired, type 'textsend', 'structsend' or 'bigsend' into the edit box 15 | of the 'Serial Monitor' and hit Enter key (or press 'Send' button) 16 | 17 | - You can use multiple clients which can be connected to one server 18 | 19 | Created 04 Mai 2020 20 | By Erich O. Pintar 21 | Modified 06 Oct 2020 22 | By Erich O. Pintar 23 | 24 | https://github.com/saghonfly/SimpleEspNowConnection 25 | 26 | */ 27 | 28 | 29 | #include "SimpleEspNowConnection.h" 30 | 31 | SimpleEspNowConnection simpleEspConnection(SimpleEspNowRole::CLIENT); 32 | 33 | String inputString; 34 | String serverAddress; 35 | 36 | typedef struct struct_message { 37 | char type; 38 | char a[32]; 39 | int b; 40 | float c; 41 | bool e; 42 | } struct_message; 43 | 44 | bool sendBigMessage() 45 | { 46 | char bigMessage[] = "\n\ 47 | There was once a woman who was very, very cheerful, though she had little to make her so; for she was old, and poor, and lonely. She lived in a little bit of a cottage and earned a scant living by running errands for her neighbours, getting a bite here, a sup there, as reward for her services. So she made shift to get on, and always looked as spry and cheery as if she had not a want in the world.\n\ 48 | Now one summer evening, as she was trotting, full of smiles as ever, along the high road to her hovel, what should she see but a big black pot lying in the ditch!\n\ 49 | \"Goodness me!\" she cried, \"that would be just the very thing for me if I only had something to put in it! But I haven't! Now who could have left it in the ditch?\"\n\ 50 | And she looked about her expecting the owner would not be far off; but she could see nobody.\n\ 51 | \"Maybe there is a hole in it,\" she went on, \"and that's why it has been cast away. But it would do fine to put a flower in for my window; so I'll just take it home with me.\"\n\ 52 | And with that she lifted the lid and looked inside. \"Mercy me!\" she cried, fair amazed. \"If it isn't full of gold pieces. Here's luck!\"\n\ 53 | And so it was, brimful of great gold coins. Well, at first she simply stood stock-still, wondering if she was standing on her head or her heels. Then she began saying:\n\ 54 | \"Lawks! But I do feel rich. I feel awful rich!\"\n\ 55 | "; 56 | 57 | return(simpleEspConnection.sendMessage(bigMessage)); 58 | } 59 | 60 | bool sendStructMessage() 61 | { 62 | struct_message myData; 63 | 64 | myData.type = '#'; // just to mark first byte. It's on you how to distinguish between struct and text message 65 | sprintf (myData.a, "Greetings from %s", simpleEspConnection.myAddress.c_str()); 66 | myData.b = random(1,20); 67 | myData.c = (float)random(1,100000)/(float)10000; 68 | myData.e = true; 69 | 70 | return(simpleEspConnection.sendMessage((uint8_t *)&myData, sizeof(myData))); 71 | } 72 | 73 | void OnSendError(uint8_t* ad) 74 | { 75 | Serial.println("SENDING TO '"+simpleEspConnection.macToStr(ad)+"' WAS NOT POSSIBLE!"); 76 | } 77 | 78 | void OnMessage(uint8_t* ad, const uint8_t* message, size_t len) 79 | { 80 | if((char)message[0] == '#') // however you distinguish.... 81 | { 82 | struct_message myData; 83 | 84 | memcpy(&myData, message, len); 85 | Serial.printf("Structure:\n"); 86 | Serial.printf("a:%s\n", myData.a); 87 | Serial.printf("b:%d\n", myData.b); 88 | Serial.printf("c:%f\n", myData.c); 89 | Serial.printf("e:%s\n", myData.e ? "true" : "false"); 90 | } 91 | else 92 | Serial.printf("MESSAGE:[%d]%s from %s\n", len, (char *)message, simpleEspConnection.macToStr(ad).c_str()); 93 | } 94 | 95 | void OnNewGatewayAddress(uint8_t *ga, String ad) 96 | { 97 | Serial.println("New GatewayAddress '"+ad+"'"); 98 | serverAddress = ad; 99 | 100 | simpleEspConnection.setServerMac(ga); 101 | } 102 | 103 | void setup() 104 | { 105 | Serial.begin(115200); 106 | Serial.println(); 107 | 108 | Serial.println("Setup..."); 109 | 110 | simpleEspConnection.begin(); 111 | // simpleEspConnection.setPairingBlinkPort(2); 112 | 113 | // serverAddress = "ECFABCC08CDA"; // Test if you know the server 114 | simpleEspConnection.setServerMac(serverAddress); 115 | simpleEspConnection.onNewGatewayAddress(&OnNewGatewayAddress); 116 | simpleEspConnection.onSendError(&OnSendError); 117 | simpleEspConnection.onMessage(&OnMessage); 118 | 119 | Serial.println(WiFi.macAddress()); 120 | } 121 | 122 | void loop() 123 | { 124 | // needed to manage the communication in the background! 125 | simpleEspConnection.loop(); 126 | 127 | while (Serial.available()) 128 | { 129 | char inChar = (char)Serial.read(); 130 | if (inChar == '\n') 131 | { 132 | Serial.println(inputString); 133 | 134 | if(inputString == "startpair") 135 | { 136 | simpleEspConnection.startPairing(30); 137 | } 138 | else if(inputString == "endpair") 139 | { 140 | simpleEspConnection.endPairing(); 141 | } 142 | else if(inputString == "changepairingmac") 143 | { 144 | uint8_t np[] {0xCE, 0x50, 0xE3, 0x15, 0xB7, 0x33}; 145 | 146 | simpleEspConnection.setPairingMac(np); 147 | } 148 | else if(inputString == "textsend") 149 | { 150 | if(!simpleEspConnection.sendMessage("This comes from the Client")) 151 | { 152 | Serial.println("SENDING TO '"+serverAddress+"' WAS NOT POSSIBLE!"); 153 | } 154 | } 155 | else if(inputString == "structsend") 156 | { 157 | if(!sendStructMessage()) 158 | { 159 | Serial.println("SENDING TO '"+serverAddress+"' WAS NOT POSSIBLE!"); 160 | } 161 | } 162 | else if(inputString == "bigsend") 163 | { 164 | if(!sendBigMessage()) 165 | { 166 | Serial.println("SENDING TO '"+serverAddress+"' WAS NOT POSSIBLE!"); 167 | } 168 | } 169 | 170 | inputString = ""; 171 | } 172 | else 173 | { 174 | inputString += inChar; 175 | } 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /examples/SimpleEspNowConnectionServer/SimpleEspNowConnectionServer.ino: -------------------------------------------------------------------------------- 1 | /* 2 | SimpleEspNowConnectionServer 3 | 4 | Basic EspNowConnection Server implementation 5 | 6 | HOWTO Arduino IDE: 7 | - Prepare two ESP8266 or ESP32 based devices (eg. WeMos) 8 | - Start two separate instances of Arduino IDE and load 9 | on the first one the 'SimpleEspNowConnectionServer.ino' and 10 | on the second one the 'SimpleEspNowConnectionClient.ino' sketch and upload 11 | these to the two ESP devices. 12 | - Start the 'Serial Monitor' in both instances and set baud rate to 115200 13 | - Type 'startpair' into the edit box of both 'Serial Monitors' and hit Enter key (or press 'Send' button) 14 | - After devices are paired, type 'textsend', 'structsend' or 'bigsend' into the edit box 15 | of the 'Serial Monitor' and hit Enter key (or press 'Send' button) 16 | 17 | - You can use multiple clients which can be connected to one server 18 | 19 | Created 04 Mai 2020 20 | By Erich O. Pintar 21 | Modified 19 Jun 2020 22 | By Erich O. Pintar 23 | 24 | https://github.com/saghonfly/SimpleEspNowConnection 25 | 26 | */ 27 | 28 | #include "SimpleEspNowConnection.h" 29 | 30 | SimpleEspNowConnection simpleEspConnection(SimpleEspNowRole::SERVER); 31 | 32 | typedef struct struct_message { 33 | char type; 34 | char a[32]; 35 | int b; 36 | float c; 37 | bool e; 38 | } struct_message; 39 | 40 | 41 | String inputString; 42 | String clientAddress; 43 | 44 | bool sendBigMessage() 45 | { 46 | char bigMessage[] = "\n\ 47 | There was once a woman who was very, very cheerful, though she had little to make her so; for she was old, and poor, and lonely. She lived in a little bit of a cottage and earned a scant living by running errands for her neighbours, getting a bite here, a sup there, as reward for her services. So she made shift to get on, and always looked as spry and cheery as if she had not a want in the world.\n\ 48 | Now one summer evening, as she was trotting, full of smiles as ever, along the high road to her hovel, what should she see but a big black pot lying in the ditch!\n\ 49 | \"Goodness me!\" she cried, \"that would be just the very thing for me if I only had something to put in it! But I haven't! Now who could have left it in the ditch?\"\n\ 50 | And she looked about her expecting the owner would not be far off; but she could see nobody.\n\ 51 | \"Maybe there is a hole in it,\" she went on, \"and that's why it has been cast away. But it would do fine to put a flower in for my window; so I'll just take it home with me.\"\n\ 52 | And with that she lifted the lid and looked inside. \"Mercy me!\" she cried, fair amazed. \"If it isn't full of gold pieces. Here's luck!\"\n\ 53 | And so it was, brimful of great gold coins. Well, at first she simply stood stock-still, wondering if she was standing on her head or her heels. Then she began saying:\n\ 54 | \"Lawks! But I do feel rich. I feel awful rich!\"\n\ 55 | \0"; 56 | 57 | return(simpleEspConnection.sendMessage(bigMessage, clientAddress)); 58 | } 59 | 60 | bool sendStructMessage() 61 | { 62 | struct_message myData; 63 | 64 | myData.type = '#'; // just to mark first byte. It's on you how to distinguish between struct and text message 65 | sprintf (myData.a, "Greetings from %s", simpleEspConnection.myAddress.c_str()); 66 | myData.b = random(1,20); 67 | myData.c = (float)random(1,100000)/(float)10000; 68 | myData.e = false; 69 | 70 | return(simpleEspConnection.sendMessage((uint8_t *)&myData, sizeof(myData), clientAddress)); 71 | } 72 | 73 | void OnSendError(uint8_t* ad) 74 | { 75 | Serial.println("SENDING TO '"+simpleEspConnection.macToStr(ad)+"' WAS NOT POSSIBLE!"); 76 | } 77 | 78 | void OnMessage(uint8_t* ad, const uint8_t* message, size_t len) 79 | { 80 | if((char)message[0] == '#') // however you distinguish.... 81 | { 82 | struct_message myData; 83 | 84 | memcpy(&myData, message, len); 85 | Serial.printf("Structure:\n"); 86 | Serial.printf("a:%s\n", myData.a); 87 | Serial.printf("b:%d\n", myData.b); 88 | Serial.printf("c:%f\n", myData.c); 89 | Serial.printf("e:%s\n", myData.e ? "true" : "false"); 90 | } 91 | else 92 | Serial.printf("MESSAGE:[%d]%s from %s\n", len, (char *)message, simpleEspConnection.macToStr(ad).c_str()); 93 | } 94 | 95 | void OnPaired(uint8_t *ga, String ad) 96 | { 97 | Serial.println("EspNowConnection : Client '"+ad+"' paired! "); 98 | simpleEspConnection.endPairing(); 99 | 100 | clientAddress = ad; 101 | } 102 | 103 | void OnConnected(uint8_t *ga, String ad) 104 | { 105 | Serial.println("EspNowConnection : Client '"+ad+"' connected! "); 106 | 107 | simpleEspConnection.sendMessage((uint8_t *)"Message at OnConnected from Server", 34, ad); 108 | } 109 | 110 | void setup() 111 | { 112 | Serial.begin(115200); 113 | Serial.println(); 114 | //clientAddress = "CC50E35B56B1"; // Test if you know the client 115 | 116 | simpleEspConnection.begin(); 117 | // simpleEspConnection.setPairingBlinkPort(2); 118 | simpleEspConnection.onMessage(&OnMessage); 119 | simpleEspConnection.onPaired(&OnPaired); 120 | simpleEspConnection.onSendError(&OnSendError); 121 | simpleEspConnection.onConnected(&OnConnected); 122 | 123 | Serial.println(WiFi.macAddress()); 124 | } 125 | 126 | void loop() 127 | { 128 | // needed to manage the communication in the background! 129 | simpleEspConnection.loop(); 130 | 131 | while (Serial.available()) 132 | { 133 | char inChar = (char)Serial.read(); 134 | if (inChar == '\n') 135 | { 136 | Serial.println(inputString); 137 | 138 | if(inputString == "startpair") 139 | { 140 | simpleEspConnection.startPairing(30); 141 | } 142 | else if(inputString == "endpair") 143 | { 144 | simpleEspConnection.endPairing(); 145 | } 146 | else if(inputString == "changepairingmac") 147 | { 148 | uint8_t np[] {0xCE, 0x50, 0xE3, 0x15, 0xB7, 0x33}; 149 | 150 | simpleEspConnection.setPairingMac(np); 151 | } 152 | else if(inputString == "textsend") 153 | { 154 | if(!simpleEspConnection.sendMessage("This comes from the server", clientAddress)) 155 | { 156 | Serial.println("SENDING TO '"+clientAddress+"' WAS NOT POSSIBLE!"); 157 | } 158 | } 159 | else if(inputString == "structsend") 160 | { 161 | if(!sendStructMessage()) 162 | { 163 | Serial.println("SENDING TO '"+clientAddress+"' WAS NOT POSSIBLE!"); 164 | } 165 | } 166 | else if(inputString == "bigsend") 167 | { 168 | if(!sendBigMessage()) 169 | { 170 | Serial.println("SENDING TO '"+clientAddress+"' WAS NOT POSSIBLE!"); 171 | } 172 | } 173 | 174 | inputString = ""; 175 | } 176 | else 177 | { 178 | inputString += inChar; 179 | } 180 | } 181 | } 182 | -------------------------------------------------------------------------------- /keywords.txt: -------------------------------------------------------------------------------- 1 | ####################################### 2 | # Syntax Coloring Map For SimpleEspNowConnection 3 | ####################################### 4 | 5 | ####################################### 6 | # Datatypes (KEYWORD1) 7 | ####################################### 8 | SimpleEspNowConnection KEYWORD1 9 | 10 | ####################################### 11 | # Methods and Functions (KEYWORD2) 12 | ####################################### 13 | 14 | 15 | begin KEYWORD2 16 | setServerMac KEYWORD2 17 | setPairingMac KEYWORD2 18 | sendMessage KEYWORD2 19 | setPairingBlinkPort KEYWORD2 20 | startPairing KEYWORD2 21 | endPairing KEYWORD2 22 | onMessage KEYWORD2 23 | onNewGatewayAddress KEYWORD2 24 | onPaired KEYWORD2 25 | onPairingFinished KEYWORD2 26 | onConnected KEYWORD2 27 | onSendError KEYWORD2 28 | onSendDone KEYWORD2 29 | macToStr KEYWORD2 30 | isSendBufferEmpty KEYWORD2 31 | loop KEYWORD2 32 | myAddress KEYWORD2 33 | 34 | ####################################### 35 | # Constants (LITERAL1) 36 | ####################################### 37 | 38 | -------------------------------------------------------------------------------- /library.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "SimpleEspNowConnection", 3 | "keywords": "espnow, pairing, communication", 4 | "description": "ESP8266 Library for EspNow connections and pairing", 5 | "repository": 6 | { 7 | "type": "git", 8 | "url": "https://github.com/saghonfly/SimpleEspNowConnection.git" 9 | }, 10 | "authors": 11 | [ 12 | { 13 | "name": "Erich O. Pintar", 14 | "email": "eop@pintarweb.net", 15 | "url": "http://pintarweb.net", 16 | "maintainer": true 17 | } 18 | ], 19 | "version": "1.2.0", 20 | "examples": "examples/*/*.ino", 21 | "frameworks": "arduino", 22 | "platforms": "*" 23 | } 24 | -------------------------------------------------------------------------------- /library.properties: -------------------------------------------------------------------------------- 1 | name=SimpleEspNowConnection 2 | version=1.2.0 3 | author=Erich O. Pintar 4 | maintainer=Erich O. Pintar 5 | sentence=EspNow Library for communication and device pairing 6 | paragraph=Supports ESP32 and ESP8266 based boards. For EspNow device pairing and communication between host and multiple clients. Supports also big messages sizes. 7 | category=Communication 8 | url=https://github.com/saghonfly/SimpleEspNowConnection 9 | architectures=* 10 | -------------------------------------------------------------------------------- /src/SimpleEspNowConnection.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | SimpleEspNowConnection.cpp - A simple EspNow connection and pairing class. 3 | Erich O. Pintar 4 | https://pintarweb.net 5 | 6 | Version : 1.2.0 7 | 8 | Created 04 Mai 2020 9 | By Erich O. Pintar 10 | Modified 06 Oct 2020 11 | By Erich O. Pintar 12 | */ 13 | 14 | #include "SimpleEspNowConnection.h" 15 | 16 | //#define DEBUG 17 | 18 | SimpleEspNowConnection::DeviceMessageBuffer::DeviceBufferObject::DeviceBufferObject() 19 | { 20 | } 21 | 22 | SimpleEspNowConnection::DeviceMessageBuffer::DeviceBufferObject::DeviceBufferObject(long id, int counter, int packages, const uint8_t *device, const uint8_t* message, size_t len) 23 | { 24 | _id = id; 25 | memcpy(_device, device, 6); 26 | memset(_message, '\0', 235); 27 | memcpy(_message, message, len); 28 | _len = len; 29 | _counter = counter; 30 | _packages = packages; 31 | } 32 | 33 | SimpleEspNowConnection::DeviceMessageBuffer::DeviceBufferObject::DeviceBufferObject(long id, int counter, int packages, const uint8_t *device) 34 | { 35 | _id = id; 36 | memcpy(_device, device, 6); 37 | memset(_message, '\0', 235); 38 | _len = 0; 39 | _counter = counter; 40 | _packages = packages; 41 | } 42 | 43 | SimpleEspNowConnection::DeviceMessageBuffer::DeviceBufferObject::~DeviceBufferObject() 44 | { 45 | } 46 | 47 | SimpleEspNowConnection::DeviceMessageBuffer::DeviceMessageBuffer() 48 | { 49 | for(int i = 0; i= packages) 96 | break; 97 | } 98 | } 99 | 100 | return true; 101 | } 102 | 103 | bool SimpleEspNowConnection::DeviceMessageBuffer::createBuffer(const uint8_t *device, const uint8_t* message, size_t len) 104 | { 105 | int packages = len / 235 + 1; 106 | int messagelen; 107 | int counter = 0; 108 | int pos = 0; 109 | long id = millis(); 110 | 111 | for(int i = 0; i 235 ? 235 : len - pos; 116 | 117 | _dbo[i] = new DeviceBufferObject(id, counter+1, packages, device, message+(counter*235), messagelen); 118 | 119 | counter++; 120 | pos+=235; 121 | 122 | if(counter >= packages) 123 | break; 124 | } 125 | } 126 | 127 | return true; 128 | } 129 | 130 | void SimpleEspNowConnection::DeviceMessageBuffer::addBuffer(const uint8_t *device, long id, uint8_t *buffer, size_t len, int package) 131 | { 132 | for(int i = 0;i_device, device, 6) == 0 && 135 | _dbo[i]->_counter == package+1 && 136 | _dbo[i]->_id == id) 137 | { 138 | memcpy(_dbo[i]->_message, buffer, len); 139 | _dbo[i]->_len = len; 140 | 141 | break; 142 | } 143 | 144 | } 145 | 146 | } 147 | 148 | size_t SimpleEspNowConnection::DeviceMessageBuffer::getBufferSize(const uint8_t *device, long id, int packages) 149 | { 150 | size_t s = 0; 151 | 152 | for(int i = 0;i_device, device, 6) == 0 && _dbo[i]->_id == id) 155 | s+=_dbo[i]->_len; 156 | } 157 | 158 | return s; 159 | } 160 | 161 | uint8_t* SimpleEspNowConnection::DeviceMessageBuffer::getBuffer(const uint8_t *device, long id, int packages, size_t len) 162 | { 163 | uint8_t* bu = new uint8_t[len]; 164 | 165 | int counter = 0; 166 | int sumlen = 0; 167 | 168 | for(int i = 0;i_device, device, 6) == 0 && _dbo[i]->_id == id) 171 | { 172 | memcpy(&bu[sumlen], _dbo[i]->_message, _dbo[i]->_len); 173 | 174 | counter++; 175 | sumlen += _dbo[i]->_len; 176 | 177 | if(counter == packages) 178 | break; 179 | } 180 | } 181 | 182 | return bu; 183 | } 184 | 185 | bool SimpleEspNowConnection::DeviceMessageBuffer::deleteBuffer(const uint8_t *device, long id) 186 | { 187 | for(int i = 0;i_device, device, 6) == 0 && _dbo[i]->_id == id) 190 | { 191 | delete _dbo[i]; 192 | _dbo[i] = NULL; 193 | } 194 | } 195 | } 196 | 197 | bool SimpleEspNowConnection::DeviceMessageBuffer::deleteBuffer(SimpleEspNowConnection::DeviceMessageBuffer::DeviceBufferObject* dbo) 198 | { 199 | for(int i = 0; i_role = role; 215 | simpleEspNowConnection = this; 216 | this->_pairingOngoing = false; 217 | memset(_serverMac,0,6); 218 | _openTransaction = false; 219 | _channel = 3; 220 | _lastSentTime = millis(); 221 | } 222 | 223 | bool SimpleEspNowConnection::begin() 224 | { 225 | _supportLooping = true; 226 | 227 | #if defined(ESP32) 228 | #if defined(DISABLE_BROWNOUT) 229 | WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0); 230 | #endif 231 | #endif 232 | 233 | WiFi.mode(WIFI_STA); 234 | 235 | WiFi.persistent(false); 236 | WiFi.macAddress(_myAddress); 237 | myAddress = macToStr(_myAddress); 238 | 239 | 240 | if (esp_now_init() != 0) 241 | { 242 | Serial.println("Error initializing ESP-NOW"); 243 | return false; 244 | } 245 | 246 | 247 | #if defined(ESP8266) 248 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 249 | #endif 250 | 251 | esp_now_register_recv_cb(SimpleEspNowConnection::onReceiveData); 252 | 253 | 254 | #if defined(ESP8266) 255 | esp_now_register_send_cb([](uint8_t* mac, uint8_t sendStatus) 256 | #elif defined(ESP32) 257 | esp_now_register_send_cb([] (const uint8_t *mac, esp_now_send_status_t sendStatus) 258 | #endif 259 | {//this is the function that is called to send data 260 | #ifdef DEBUG 261 | Serial.printf("--- send_cb, send done, status = %i\n", sendStatus); 262 | #endif 263 | simpleEspNowConnection->_lastSentTime = millis(); 264 | simpleEspNowConnection->_openTransaction = false; 265 | 266 | if(memcmp(mac, simpleEspNowConnection->_pairingMac, 6) != 0) 267 | { 268 | if(sendStatus != 0 && simpleEspNowConnection->_SendErrorFunction != NULL) 269 | { 270 | simpleEspNowConnection->_SendErrorFunction((uint8_t*)mac); 271 | } 272 | if(sendStatus == 0 && simpleEspNowConnection->_SendDoneFunction != NULL) 273 | { 274 | simpleEspNowConnection->_SendDoneFunction((uint8_t*)mac); 275 | } 276 | } 277 | }); 278 | 279 | if(this->_role == SimpleEspNowRole::SERVER) 280 | { 281 | initServer(); 282 | } 283 | else 284 | { 285 | initClient(); 286 | } 287 | 288 | return true; 289 | } 290 | 291 | bool SimpleEspNowConnection::setPairingBlinkPort(int pairingGPIO, bool invers) 292 | { 293 | _pairingGPIO = pairingGPIO; 294 | _pairingInvers = invers; 295 | 296 | pinMode(_pairingGPIO, OUTPUT); 297 | digitalWrite(_pairingGPIO, _pairingInvers); 298 | 299 | return true; 300 | } 301 | 302 | void SimpleEspNowConnection::pairingTickerServer() 303 | { 304 | #ifdef DEBUG 305 | Serial.println("EspNowConnection::Pairing request sent..."+ 306 | String(simpleEspNowConnection->_pairingCounter+1)+"/"+ 307 | String(simpleEspNowConnection->_pairingMaxCount)); 308 | #endif 309 | 310 | char sendMessage[13]; 311 | long id = millis(); 312 | 313 | sendMessage[0] = SimpleEspNowMessageType::PAIR; // Type of message 314 | sendMessage[1] = 1; // 1st package 315 | sendMessage[2] = 1; // from 1 package. WIll be enhanced in one of the next versions 316 | memcpy(sendMessage+3, &id, 4); 317 | 318 | memcpy(sendMessage+7, simpleEspNowConnection->_myAddress, 6); 319 | 320 | #if defined(ESP32) 321 | memcpy(&simpleEspNowConnection->_clientMacPeerInfo.peer_addr, simpleEspNowConnection->_pairingMac, 6); 322 | esp_now_add_peer(&simpleEspNowConnection->_clientMacPeerInfo); 323 | #endif 324 | 325 | 326 | esp_now_send(simpleEspNowConnection->_pairingMac, 327 | (uint8_t *)sendMessage, 328 | 9); 329 | 330 | #if defined(ESP32) 331 | esp_now_del_peer(simpleEspNowConnection->_pairingMac); 332 | #endif 333 | 334 | if(simpleEspNowConnection->_pairingMaxCount > 0) 335 | { 336 | simpleEspNowConnection->_pairingCounter++; 337 | if(simpleEspNowConnection->_pairingCounter >= simpleEspNowConnection->_pairingMaxCount) 338 | { 339 | simpleEspNowConnection->endPairing(); 340 | } 341 | } 342 | } 343 | 344 | void SimpleEspNowConnection::pairingTickerClient() 345 | { 346 | digitalWrite(simpleEspNowConnection->_pairingGPIO, simpleEspNowConnection->_pairingInvers); 347 | simpleEspNowConnection->endPairing(); 348 | simpleEspNowConnection->_pairingTickerBlink.detach(); 349 | 350 | #if defined(ESP8266) 351 | wifi_set_macaddr(STATION_IF, &simpleEspNowConnection->_myAddress[0]); 352 | #elif defined(ESP32) 353 | esp_wifi_set_mac(WIFI_IF_STA, &simpleEspNowConnection->_myAddress[0]); 354 | #endif 355 | 356 | #ifdef DEBUG 357 | Serial.println("MAC set to : "+WiFi.macAddress()); 358 | #endif 359 | } 360 | 361 | void SimpleEspNowConnection::pairingTickerLED() 362 | { 363 | if(simpleEspNowConnection->_pairingOngoing) 364 | { 365 | int state = digitalRead(simpleEspNowConnection->_pairingGPIO); 366 | digitalWrite(simpleEspNowConnection->_pairingGPIO, !state); 367 | } 368 | else 369 | { 370 | digitalWrite(simpleEspNowConnection->_pairingGPIO, simpleEspNowConnection->_pairingInvers); 371 | simpleEspNowConnection->_pairingTickerBlink.detach(); 372 | } 373 | } 374 | 375 | bool SimpleEspNowConnection::startPairing(int timeoutSec) 376 | { 377 | if(_pairingOngoing) return false; 378 | 379 | if(timeoutSec > 0 && timeoutSec < 5) 380 | timeoutSec = 5; 381 | 382 | if(this->_role == SimpleEspNowRole::SERVER) 383 | { 384 | #ifdef DEBUG 385 | Serial.println("SimpleEspNowConnection::Server Pairing started"); 386 | #endif 387 | 388 | _pairingOngoing = true; 389 | _pairingCounter = 0; 390 | if(timeoutSec > 0) 391 | { 392 | _pairingMaxCount = timeoutSec / 5; 393 | } 394 | else 395 | { 396 | _pairingMaxCount = 0; 397 | } 398 | 399 | _pairingTicker.attach(5.0, SimpleEspNowConnection::pairingTickerServer); 400 | 401 | if(_pairingGPIO != -1) 402 | { 403 | _pairingTickerBlink.attach(0.5, pairingTickerLED); 404 | } 405 | } 406 | else 407 | { 408 | #ifdef DEBUG 409 | Serial.println("SimpleEspNowConnection::Client Pairing started"); 410 | #endif 411 | 412 | _pairingOngoing = true; 413 | 414 | #if defined(ESP8266) 415 | wifi_set_macaddr(STATION_IF, &_pairingMac[0]); 416 | #elif defined(ESP32) 417 | esp_wifi_set_mac(WIFI_IF_STA, &_pairingMac[0]); 418 | #endif 419 | 420 | #ifdef DEBUG 421 | Serial.println("MAC set to : "+WiFi.macAddress()); 422 | #endif 423 | 424 | if(timeoutSec == 0) 425 | timeoutSec = 30; 426 | else if(timeoutSec < 10) 427 | timeoutSec = 10; 428 | else if(timeoutSec > 120) 429 | timeoutSec = 120; 430 | 431 | _pairingTicker.attach(timeoutSec, SimpleEspNowConnection::pairingTickerClient); 432 | 433 | if(_pairingGPIO != -1) 434 | { 435 | _pairingTickerBlink.attach(0.5, pairingTickerLED); 436 | } 437 | } 438 | 439 | return true; 440 | } 441 | 442 | bool SimpleEspNowConnection::endPairing() 443 | { 444 | _pairingOngoing = false; 445 | _pairingTicker.detach(); 446 | _pairingTickerBlink.detach(); 447 | 448 | if(_pairingGPIO != -1) 449 | digitalWrite(_pairingGPIO, _pairingInvers); 450 | 451 | 452 | #ifdef DEBUG 453 | if(_role == SimpleEspNowRole::SERVER) 454 | Serial.println("EspNowConnection::Server Pairing endet"); 455 | else 456 | Serial.println("EspNowConnection::Client Pairing endet"); 457 | #endif 458 | 459 | if(_PairingFinishedFunction != NULL) 460 | { 461 | _PairingFinishedFunction(); 462 | } 463 | 464 | return true; 465 | } 466 | 467 | void SimpleEspNowConnection::prepareSendPackages(uint8_t* message, size_t len, String address) 468 | { 469 | uint8_t *mac = strToMac(address.c_str()); 470 | 471 | deviceSendMessageBuffer.createBuffer(mac, message, len); 472 | } 473 | 474 | bool SimpleEspNowConnection::sendMessage(char* message, String address) 475 | { 476 | return sendMessage((uint8_t*)message, strlen(message)+1, address); 477 | } 478 | 479 | bool SimpleEspNowConnection::sendMessage(uint8_t* message, size_t len, String address) 480 | { 481 | if( (_role == SimpleEspNowRole::SERVER && address.length() != 12 ) || 482 | (_role == SimpleEspNowRole::CLIENT && _serverMac[0] == 0 )) 483 | { 484 | return false; 485 | } 486 | 487 | int packages = len / 235 + 1; 488 | 489 | if(!_supportLooping && packages > 1) 490 | return false; 491 | 492 | #ifdef DEBUG 493 | Serial.printf("Number of bytes %d, number of packages %d\n", len, packages); 494 | #endif 495 | 496 | if(!_supportLooping) 497 | return sendMessageOld(message, address); 498 | 499 | if(_role == SimpleEspNowRole::CLIENT) 500 | address = macToStr(_serverMac); 501 | 502 | prepareSendPackages(message, len, address); 503 | 504 | return true; 505 | } 506 | 507 | bool SimpleEspNowConnection::sendPackage(long id, int package, int sum, uint8_t* message, size_t messagelen, uint8_t* address) 508 | { 509 | char sendMessage[messagelen+7]; 510 | 511 | sendMessage[0] = SimpleEspNowMessageType::DATA; // Type of message 512 | sendMessage[1] = package; 513 | sendMessage[2] = sum; 514 | memcpy(sendMessage+3, &id, 4); 515 | memcpy(sendMessage+7, message, messagelen); 516 | 517 | if(_role == SimpleEspNowRole::SERVER) 518 | { 519 | #if defined(ESP32) 520 | memcpy(&simpleEspNowConnection->_clientMacPeerInfo.peer_addr, address, 6); 521 | esp_now_add_peer(&simpleEspNowConnection->_clientMacPeerInfo); 522 | #elif defined(ESP8266) 523 | esp_now_add_peer(address, ESP_NOW_ROLE_COMBO, simpleEspNowConnection->_channel, NULL, 0); 524 | #endif 525 | 526 | esp_now_send(address, (uint8_t *) sendMessage, messagelen+7); 527 | _openTransaction = true; 528 | 529 | esp_now_del_peer(address); 530 | } 531 | else 532 | { 533 | _openTransaction = true; 534 | esp_now_send(address, (uint8_t *) sendMessage, messagelen+7); 535 | } 536 | 537 | return true; 538 | } 539 | 540 | bool SimpleEspNowConnection::sendMessageOld(uint8_t* message, String address) 541 | { 542 | if( (_role == SimpleEspNowRole::SERVER && address.length() != 12 ) || 543 | (_role == SimpleEspNowRole::CLIENT && _serverMac[0] == 0 ) || 544 | sizeof(message) > 235) 545 | { 546 | return false; 547 | } 548 | 549 | uint8_t sendMessage[sizeof(message)+7]; 550 | long id = millis(); 551 | 552 | sendMessage[0] = SimpleEspNowMessageType::DATA; // Type of message 553 | sendMessage[1] = 1; // 1st package 554 | sendMessage[2] = 1; // from 1 package. WIll be enhanced in one of the next versions 555 | memcpy(sendMessage+3, &id, 4); 556 | memcpy(sendMessage+7, message, sizeof(message)); 557 | 558 | #if defined(ESP8266) 559 | esp_now_register_send_cb([](uint8_t* mac, uint8_t sendStatus) 560 | #elif defined(ESP32) 561 | esp_now_register_send_cb([] (const uint8_t *mac, esp_now_send_status_t sendStatus) 562 | #endif 563 | {//this is the function that is called to send data 564 | #ifdef DEBUG 565 | Serial.printf("send_cb, send done, status = %i\n", sendStatus); 566 | #endif 567 | esp_now_unregister_send_cb(); 568 | 569 | if(sendStatus != 0 && simpleEspNowConnection->_SendErrorFunction != NULL) 570 | { 571 | simpleEspNowConnection->_SendErrorFunction((uint8_t*)mac); 572 | } 573 | if(sendStatus == 0 && simpleEspNowConnection->_SendDoneFunction != NULL) 574 | { 575 | simpleEspNowConnection->_SendDoneFunction((uint8_t*)mac); 576 | } 577 | }); 578 | 579 | 580 | if(_role == SimpleEspNowRole::SERVER) 581 | { 582 | uint8_t *mac = strToMac(address.c_str()); 583 | 584 | #if defined(ESP32) 585 | memcpy(&simpleEspNowConnection->_clientMacPeerInfo.peer_addr, mac, 6); 586 | esp_now_add_peer(&simpleEspNowConnection->_clientMacPeerInfo); 587 | #endif 588 | esp_now_send(mac, (uint8_t *) sendMessage, sizeof(sendMessage)); 589 | #if defined(ESP32) 590 | esp_now_del_peer(mac); 591 | #endif 592 | 593 | delete mac; 594 | } 595 | else 596 | { 597 | esp_now_send(_serverMac, (uint8_t *) sendMessage, sizeof(sendMessage)); 598 | } 599 | 600 | return true; 601 | } 602 | uint8_t* SimpleEspNowConnection::strToMac(const char* str) 603 | { 604 | if(strlen(str) != 12) 605 | return NULL; 606 | 607 | uint8_t* mac = new uint8_t[6]; 608 | 609 | char buffer[2]; 610 | 611 | for(int i = 0; i<6; i++) 612 | { 613 | strncpy ( buffer, str+i*2, 2 ); 614 | 615 | mac[i] = strtol(buffer, NULL, 16); 616 | } 617 | 618 | return mac; 619 | } 620 | 621 | String SimpleEspNowConnection::macToStr(const uint8_t* mac) 622 | { 623 | char macAddr[13]; 624 | macAddr[12] = 0; 625 | 626 | sprintf(macAddr, "%02X%02X%02X%02X%02X%02X", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]); 627 | 628 | return String(macAddr); 629 | } 630 | 631 | 632 | #if defined(ESP8266) 633 | void SimpleEspNowConnection::onReceiveData(uint8_t *mac, uint8_t *data, uint8_t len) 634 | #elif defined(ESP32) 635 | void SimpleEspNowConnection::onReceiveData(const uint8_t *mac, const uint8_t *data, int len) 636 | #endif 637 | { 638 | if(len <= 7) 639 | return; 640 | 641 | uint8_t buffer[len-6]; 642 | buffer[len-7] = 0; 643 | long id; 644 | 645 | memcpy(buffer, data+7, len-7); 646 | memcpy(&id, data+3, 4); 647 | 648 | if(simpleEspNowConnection->_role == SimpleEspNowRole::CLIENT && 649 | simpleEspNowConnection->_pairingOngoing) 650 | { 651 | if(data[0] == SimpleEspNowMessageType::PAIR) 652 | { 653 | if(simpleEspNowConnection->_NewGatewayAddressFunction) 654 | { 655 | #if defined(ESP8266) 656 | wifi_set_macaddr(STATION_IF, &simpleEspNowConnection->_myAddress[0]); 657 | #elif defined(ESP32) 658 | esp_wifi_set_mac(WIFI_IF_STA, &simpleEspNowConnection->_myAddress[0]); 659 | #endif 660 | simpleEspNowConnection->endPairing(); 661 | simpleEspNowConnection->_NewGatewayAddressFunction((uint8_t *)mac, String(simpleEspNowConnection->macToStr((uint8_t *)mac))); 662 | 663 | uint8_t sendMessage[13]; 664 | long ids = millis(); 665 | 666 | sendMessage[0] = SimpleEspNowMessageType::PAIR; // Type of message 667 | sendMessage[1] = 1; // 1st package 668 | sendMessage[2] = 1; // from 1 package. Will be enhanced in one of the next versions 669 | memcpy(sendMessage+3, &ids, 4); 670 | 671 | memcpy(sendMessage+7, simpleEspNowConnection->_myAddress, 6); 672 | 673 | esp_now_send(mac, (uint8_t *) sendMessage, sizeof(sendMessage)); 674 | } 675 | } 676 | } 677 | else 678 | { 679 | if(data[0] == SimpleEspNowMessageType::DATA && simpleEspNowConnection->_MessageFunction) 680 | { 681 | #ifdef DEBUG 682 | Serial.printf("Package %d of %d packages\n", data[1], data[2]); 683 | #endif 684 | 685 | if(data[1] == 1 && data[2] > 1) // prepare memory for this device 686 | { 687 | simpleEspNowConnection->deviceReceiveMessageBuffer.createBuffer(mac, id, data[2]); 688 | simpleEspNowConnection->deviceReceiveMessageBuffer.addBuffer(mac, id, (uint8_t *)buffer, len-7, data[1]-1); 689 | } 690 | else if(data[2] > 1) 691 | { 692 | simpleEspNowConnection->deviceReceiveMessageBuffer.addBuffer(mac, id, (uint8_t *)buffer, len-7, data[1]-1); 693 | } 694 | 695 | if(data[1] == data[2]) 696 | { 697 | if(data[2] > 1) 698 | { 699 | size_t blen = simpleEspNowConnection->deviceReceiveMessageBuffer.getBufferSize(mac, id, data[2]); 700 | uint8_t *bb = simpleEspNowConnection->deviceReceiveMessageBuffer.getBuffer(mac, id, data[2], blen); 701 | 702 | simpleEspNowConnection->_MessageFunction( (uint8_t *)mac, 703 | bb, 704 | blen); 705 | delete(bb); 706 | simpleEspNowConnection->deviceReceiveMessageBuffer.deleteBuffer(mac, id); 707 | } 708 | else 709 | simpleEspNowConnection->_MessageFunction((uint8_t *)mac, buffer, len-7); 710 | } 711 | } 712 | if(simpleEspNowConnection->_PairedFunction) 713 | { 714 | if(data[0] == SimpleEspNowMessageType::PAIR) 715 | simpleEspNowConnection->_PairedFunction((uint8_t *)mac, String(simpleEspNowConnection->macToStr((uint8_t *)buffer))); 716 | } 717 | if(simpleEspNowConnection->_ConnectedFunction) 718 | { 719 | if(data[0] == SimpleEspNowMessageType::CONNECT) 720 | simpleEspNowConnection->_ConnectedFunction((uint8_t *)mac, String(simpleEspNowConnection->macToStr((uint8_t *)buffer))); 721 | } 722 | 723 | #ifdef DEBUG 724 | Serial.println("SimpleEspNowConnection::message arrived from : "+simpleEspNowConnection->macToStr((uint8_t *)mac)); 725 | #endif 726 | } 727 | } 728 | 729 | bool SimpleEspNowConnection::setPairingMac(uint8_t* mac) 730 | { 731 | memcpy(_pairingMac, mac, 6); 732 | 733 | return true; 734 | } 735 | 736 | bool SimpleEspNowConnection::setServerMac(String address) 737 | { 738 | if(address.length() != 12) 739 | return false; 740 | 741 | uint8_t *mac = strToMac(address.c_str()); 742 | 743 | if(mac == NULL) 744 | return false; 745 | 746 | setServerMac(mac); 747 | 748 | delete mac; 749 | 750 | return true; 751 | } 752 | 753 | bool SimpleEspNowConnection::setServerMac(uint8_t* mac) 754 | { 755 | if(this->_role == SimpleEspNowRole::SERVER) 756 | return false; 757 | 758 | memcpy(_serverMac, mac, 6); 759 | simpleEspNowConnection->macToStr(_serverMac); 760 | 761 | #ifdef DEBUG 762 | Serial.println("EspNowConnection::setServerMac to "+simpleEspNowConnection->macToStr(_serverMac)); 763 | #endif 764 | 765 | char sendMessage[13]; 766 | long ids = millis(); 767 | 768 | 769 | 770 | sendMessage[0] = SimpleEspNowMessageType::CONNECT; // Type of message 771 | sendMessage[1] = 1; // 1st package 772 | sendMessage[2] = 1; // from 1 package. WIll be enhanced in one of the next versions 773 | memcpy(sendMessage+3, &ids, 4); 774 | 775 | 776 | sendMessage[14] = 0; 777 | 778 | memcpy(sendMessage+7, simpleEspNowConnection->_myAddress, 6); 779 | 780 | #if defined(ESP32) 781 | memcpy(&simpleEspNowConnection->_serverMacPeerInfo.peer_addr, _serverMac, 6); 782 | esp_now_add_peer(&simpleEspNowConnection->_serverMacPeerInfo); 783 | #endif 784 | 785 | esp_now_send(mac, (uint8_t *) sendMessage, sizeof(sendMessage)); 786 | 787 | return true; 788 | } 789 | 790 | void SimpleEspNowConnection::onPaired(PairedFunction fn) 791 | { 792 | _PairedFunction = fn; 793 | } 794 | 795 | void SimpleEspNowConnection::onMessage(MessageFunction fn) 796 | { 797 | _MessageFunction = fn; 798 | } 799 | 800 | void SimpleEspNowConnection::onNewGatewayAddress(NewGatewayAddressFunction fn) 801 | { 802 | _NewGatewayAddressFunction = fn; 803 | } 804 | 805 | void SimpleEspNowConnection::onConnected(ConnectedFunction fn) 806 | { 807 | _ConnectedFunction = fn; 808 | } 809 | 810 | void SimpleEspNowConnection::onPairingFinished(PairingFinishedFunction fn) 811 | { 812 | _PairingFinishedFunction = fn; 813 | } 814 | 815 | void SimpleEspNowConnection::onSendError(SendErrorFunction fn) 816 | { 817 | _SendErrorFunction = fn; 818 | } 819 | 820 | void SimpleEspNowConnection::onSendDone(SendDoneFunction fn) 821 | { 822 | _SendDoneFunction = fn; 823 | } 824 | 825 | bool SimpleEspNowConnection::isSendBufferEmpty() 826 | { 827 | return deviceSendMessageBuffer.isSendBufferEmpty() && !_openTransaction; 828 | } 829 | 830 | bool SimpleEspNowConnection::loop() 831 | { 832 | SimpleEspNowConnection::DeviceMessageBuffer::DeviceBufferObject *dbo = deviceSendMessageBuffer.getNextBuffer(); 833 | 834 | if(dbo == NULL) 835 | return false; 836 | if(simpleEspNowConnection->_openTransaction) 837 | return true; 838 | 839 | sendPackage(dbo->_id, dbo->_counter, dbo->_packages, dbo->_message, dbo->_len, dbo->_device); 840 | 841 | deviceSendMessageBuffer.deleteBuffer(dbo); 842 | 843 | return !deviceSendMessageBuffer.isSendBufferEmpty(); 844 | } 845 | 846 | bool SimpleEspNowConnection::initServer() 847 | { 848 | 849 | #ifdef DEBUG 850 | Serial.println("SimpleEspNowConnection: Server initialized"); 851 | #endif 852 | 853 | return true; 854 | } 855 | 856 | bool SimpleEspNowConnection::initClient() 857 | { 858 | 859 | #ifdef DEBUG 860 | Serial.println("SimpleEspNowConnection: Client initialized"); 861 | #endif 862 | 863 | return true; 864 | } 865 | -------------------------------------------------------------------------------- /src/SimpleEspNowConnection.h: -------------------------------------------------------------------------------- 1 | /* 2 | SimpleEspNowConnection.h - A simple EspNow connection and pairing class. 3 | Erich O. Pintar 4 | https://pintarweb.net 5 | 6 | Version : 1.2.0 7 | 8 | Created 04 Mai 2020 9 | By Erich O. Pintar 10 | Modified 06 Oct 2020 11 | By Erich O. Pintar 12 | */ 13 | 14 | #ifndef SIMPLEESPNOWCONNECTION_H 15 | #define SIMPLEESPNOWCONNECTION_H 16 | #include 17 | 18 | #define DISABLE_BROWNOUT 19 | 20 | #if defined(ESP8266) 21 | #include 22 | extern "C" { 23 | #include 24 | #include 25 | } 26 | #elif defined(ESP32) 27 | #include 28 | #include 29 | #include 30 | 31 | #if defined(DISABLE_BROWNOUT) 32 | #include "soc/soc.h" 33 | #include "soc/rtc_cntl_reg.h" 34 | #endif 35 | #endif 36 | 37 | #include "Ticker.h" 38 | 39 | #define MaxBufferSize 50 40 | 41 | typedef enum SimpleEspNowRole 42 | { 43 | SERVER = 0, CLIENT = 1 44 | } SimpleEspNowRole_t; 45 | 46 | class SimpleEspNowConnection 47 | { 48 | public: 49 | typedef std::function MessageFunction; 50 | typedef std::function NewGatewayAddressFunction; 51 | typedef std::function PairedFunction; 52 | typedef std::function ConnectedFunction; 53 | typedef std::function SendErrorFunction; 54 | typedef std::function SendDoneFunction; 55 | typedef std::function PairingFinishedFunction; 56 | 57 | SimpleEspNowConnection(SimpleEspNowRole role); 58 | 59 | bool begin(); 60 | bool loop(); 61 | bool isSendBufferEmpty(); 62 | bool setServerMac(uint8_t* mac); 63 | bool setServerMac(String address); 64 | bool setPairingMac(uint8_t* mac); 65 | bool sendMessage(uint8_t* message, size_t len, String address = ""); 66 | bool sendMessage(char* message, String address = ""); 67 | bool sendMessageOld(uint8_t* message, String address = ""); 68 | bool setPairingBlinkPort(int pairingGPIO, bool invers = true); 69 | bool startPairing(int timeoutSec = 0); 70 | bool endPairing(); 71 | 72 | void onMessage(MessageFunction fn); 73 | void onNewGatewayAddress(NewGatewayAddressFunction fn); 74 | void onPaired(PairedFunction fn); 75 | void onConnected(ConnectedFunction fn); 76 | void onSendError(SendErrorFunction fn); 77 | void onSendDone(SendDoneFunction fn); 78 | void onPairingFinished(PairingFinishedFunction fn); 79 | 80 | String macToStr(const uint8_t* mac); 81 | String myAddress; 82 | 83 | protected: 84 | typedef enum SimpleEspNowMessageType 85 | { 86 | DATA = 1, PAIR = 2, CONNECT = 3 87 | } SimpleEspNowMessageType_t; 88 | 89 | class DeviceMessageBuffer 90 | { 91 | public: 92 | class DeviceBufferObject 93 | { 94 | public: 95 | DeviceBufferObject(); 96 | DeviceBufferObject(long id, int counter, int packages, const uint8_t *device); 97 | DeviceBufferObject(long id, int counter, int packages, const uint8_t *device, const uint8_t* message, size_t len); 98 | ~DeviceBufferObject(); 99 | 100 | long _id; 101 | uint8_t _device[6]; 102 | uint8_t _message[235]; 103 | size_t _len; 104 | int _counter; 105 | int _packages; 106 | }; 107 | 108 | DeviceMessageBuffer(); 109 | ~DeviceMessageBuffer(); 110 | 111 | bool createBuffer(const uint8_t *device, const uint8_t* message, size_t len); 112 | bool createBuffer(const uint8_t *device, long id, int packages); 113 | void addBuffer(const uint8_t *device, long id, uint8_t *buffer, size_t len, int package); 114 | uint8_t* getBuffer(const uint8_t *device, long id, int packages, size_t len); 115 | size_t getBufferSize(const uint8_t *device, long id, int packages); 116 | SimpleEspNowConnection::DeviceMessageBuffer::DeviceBufferObject* getNextBuffer(); 117 | bool isSendBufferEmpty(); 118 | bool deleteBuffer(SimpleEspNowConnection::DeviceMessageBuffer::DeviceBufferObject* dbo); 119 | bool deleteBuffer(const uint8_t *device, long id); 120 | 121 | DeviceBufferObject *_dbo[MaxBufferSize]; // buffer for messages 122 | }; 123 | 124 | DeviceMessageBuffer deviceSendMessageBuffer; 125 | DeviceMessageBuffer deviceReceiveMessageBuffer; 126 | 127 | private: 128 | SimpleEspNowRole_t _role; 129 | 130 | uint8_t _pairingMac[6] {0xCE, 0x50, 0xE3, 0x15, 0xB7, 0x34}; // MAC ADDRESS WHEN CLIENT IS LISTENING FOR PAIRING 131 | uint8_t _myAddress[6]; 132 | uint8_t _serverMac[6]; 133 | uint8_t _channel; 134 | volatile long _lastSentTime; 135 | 136 | bool initServer(); 137 | bool initClient(); 138 | void prepareSendPackages(uint8_t* message, size_t len, String address); 139 | bool sendPackage(long id, int package, int sum, uint8_t* message, size_t messagelen, uint8_t* address); 140 | 141 | uint8_t* strToMac(const char* str); 142 | 143 | #if defined(ESP8266) 144 | static void onReceiveData(uint8_t *mac, uint8_t *data, uint8_t len); 145 | #elif defined(ESP32) 146 | static void onReceiveData(const uint8_t *mac, const uint8_t *data, int len); 147 | #endif 148 | static void pairingTickerServer(); 149 | static void pairingTickerClient(); 150 | static void pairingTickerLED(); 151 | 152 | Ticker _pairingTicker, _pairingTickerBlink; 153 | volatile bool _pairingOngoing; 154 | volatile int _pairingCounter; 155 | volatile int _pairingMaxCount; 156 | bool _supportLooping; 157 | volatile bool _openTransaction; 158 | 159 | int _pairingGPIO = -1; 160 | int _pairingInvers = true; 161 | 162 | MessageFunction _MessageFunction = NULL; 163 | NewGatewayAddressFunction _NewGatewayAddressFunction = NULL; 164 | PairedFunction _PairedFunction = NULL; 165 | ConnectedFunction _ConnectedFunction = NULL; 166 | SendErrorFunction _SendErrorFunction = NULL; 167 | SendDoneFunction _SendDoneFunction = NULL; 168 | PairingFinishedFunction _PairingFinishedFunction = NULL; 169 | 170 | #if defined(ESP32) 171 | esp_now_peer_info_t _serverMacPeerInfo; 172 | esp_now_peer_info_t _clientMacPeerInfo; 173 | #endif 174 | }; 175 | 176 | static SimpleEspNowConnection *simpleEspNowConnection = NULL; 177 | #endif 178 | --------------------------------------------------------------------------------