├── EspNowFloodingMesh.cpp ├── EspNowFloodingMesh.h ├── LICENSE ├── README.md ├── espnowBroadcast.cpp ├── espnowBroadcast.h ├── examples ├── Broadcast │ └── espnowboradcast │ │ └── espnowboradcast.ino ├── askReply │ ├── master │ │ └── master.ino │ └── slave │ │ └── slave.ino ├── flooding mesh │ ├── master │ │ └── master.ino │ └── slave │ │ └── slave.ino └── start-network │ ├── master │ └── master.ino │ └── slave │ └── slave.ino ├── keywords.txt ├── library.json ├── library.properties ├── safememcpy.cpp ├── safememcpy.h ├── wifi802_11.cpp └── wifi802_11.h /EspNowFloodingMesh.cpp: -------------------------------------------------------------------------------- 1 | #ifdef ESP32 2 | #ifndef USE_RAW_801_11 3 | #include 4 | #include 5 | #endif 6 | #include 7 | #include "mbedtls/aes.h" 8 | #else 9 | #include 10 | #include "AESLib.h" // From https://github.com/kakopappa/arduino-esp8266-aes-lib 11 | #endif 12 | 13 | #ifndef USE_RAW_801_11 14 | #include "espnowBroadcast.h" 15 | #endif 16 | #include "EspNowFloodingMesh.h" 17 | #include 18 | 19 | #ifdef USE_RAW_801_11 20 | #include "wifi802_11.h" 21 | #endif 22 | 23 | #define AES_BLOCK_SIZE 16 24 | #define DISPOSABLE_KEY_LENGTH AES_BLOCK_SIZE 25 | #define REJECTED_LIST_SIZE 50 26 | #define REQUEST_REPLY_DATA_BASE_SIZE 30 27 | 28 | #define ALLOW_TIME_ERROR_IN_SYNC_MESSAGE false //Decrease security. false=Validate sync messages against own RTC time 29 | 30 | // 10 - 300 sec 31 | #define RESEND_SYNC_TIME_MS 15000 32 | 33 | #define USER_MSG 1 34 | #define SYNC_TIME_MSG 2 35 | #define INSTANT_TIME_SYNC_REQ 3 36 | #define USER_REQUIRE_RESPONSE_MSG 4 37 | #define USER_REQUIRE_REPLY_MSG 5 38 | #define INSTANT_TIME_SYNC_REQ_ANNONCE 7 39 | 40 | 41 | unsigned char ivKey[16] = {0xb2, 0x4b, 0xf2, 0xf7, 0x7a, 0xc5, 0xec, 0x0c, 0x5e, 0x1f, 0x4d, 0xc1, 0xae, 0x46, 0x5e, 0x75}; 42 | 43 | bool masterFlag = false; 44 | bool syncronized = false; 45 | bool batteryNode = false; 46 | bool timeStampCheckDisabled = false; 47 | uint8_t syncTTL = 0; 48 | bool isespNowFloodingMeshInitialized = false; 49 | int myBsid = 0x112233; 50 | 51 | telemetry_stats_st telemetry_stats; 52 | 53 | #pragma pack(push,1) 54 | struct header { 55 | uint8_t msgId; 56 | uint8_t length; 57 | uint32_t p1; 58 | time_t time; 59 | }; 60 | 61 | struct mesh_secred_part{ 62 | struct header header; 63 | uint8_t data[240]; 64 | }; 65 | 66 | struct mesh_unencrypted_part { 67 | unsigned char bsid[3]; 68 | uint8_t ttl; 69 | uint16_t crc16; 70 | void setBsid(uint32_t v) { 71 | bsid[0]=(v>>(16))&0xff; 72 | bsid[1]=(v>>(8))&0xff; 73 | bsid[2]=v&0xff; 74 | } 75 | void set(const uint8_t *v) { 76 | memcpy(this,v,sizeof(struct mesh_unencrypted_part)); 77 | } 78 | uint32_t getBsid(){ 79 | uint32_t ret=0; 80 | ret|=((uint32_t)bsid[0])<<16; 81 | ret|=((uint32_t)bsid[1])<<8; 82 | ret|=((uint32_t)bsid[2]); 83 | return ret; 84 | } 85 | }; 86 | 87 | typedef struct mesh_unencrypted_part unencrypted_t; 88 | #define SECRED_PART_OFFSET sizeof(unencrypted_t) 89 | 90 | 91 | struct meshFrame{ 92 | unencrypted_t unencrypted; 93 | struct mesh_secred_part encrypted; 94 | }; 95 | #pragma pack(pop) 96 | 97 | int espNowFloodingMesh_getTTL() { 98 | return syncTTL; 99 | } 100 | 101 | const unsigned char broadcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 102 | uint8_t aes_secredKey[] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE, 0xFF}; 103 | 104 | bool forwardMsg(const uint8_t *data, int len); 105 | uint32_t sendMsg(uint8_t* msg, int size, int ttl, int msgId, void *ptr=NULL); 106 | void hexDump(const uint8_t*b,int len); 107 | static void (*espNowFloodingMesh_receive_cb)(const uint8_t *, int, uint32_t) = NULL; 108 | 109 | uint16_t calculateCRC(int c, const unsigned char*b, int len); 110 | uint16_t calculateCRC(struct meshFrame *m); 111 | int decrypt(const uint8_t *_from, struct meshFrame *m, int size); 112 | bool compareTime(time_t current, time_t received, time_t maxDifference); 113 | 114 | 115 | 116 | void (*errorPrintCB)(int,const char *) = NULL; 117 | 118 | void espNowFloodingMesh_ErrorDebugCB(void (*callback)(int, const char *)) { 119 | errorPrintCB = callback; 120 | } 121 | 122 | void espNowFloodingMesh_disableTimeDifferenceCheck(bool disable) { 123 | timeStampCheckDisabled = disable; 124 | if (disable) { 125 | syncronized = true; 126 | } 127 | } 128 | 129 | int8_t led_pin = -1; 130 | bool led_is_on = false; 131 | uint8_t led_blink_mode = 0; 132 | uint32_t recv_packet_ts = 0; 133 | uint32_t tx_packet_ts = 0; 134 | 135 | void espNowFloodingMesh_enableBlink(int8_t pin, uint8_t mode) { 136 | led_pin = pin; 137 | led_blink_mode = mode; 138 | pinMode(led_pin, OUTPUT); 139 | digitalWrite(led_pin, HIGH); // turn off 140 | } 141 | 142 | void print(int level, const char * format, ... ) { 143 | if (errorPrintCB) { 144 | static char buffer[256]; 145 | va_list args; 146 | va_start (args, format); 147 | vsprintf (buffer,format, args); 148 | 149 | errorPrintCB(level, buffer); 150 | 151 | va_end (args); 152 | } 153 | } 154 | 155 | 156 | void espNowFloodingMesh_setAesInitializationVector(const unsigned char iv[16]) { 157 | memcpy(ivKey, iv, sizeof(ivKey)); 158 | } 159 | 160 | void espNowFloodingMesh_setToBatteryNode(bool isBatteryNode) { 161 | batteryNode = isBatteryNode; 162 | } 163 | 164 | telemetry_stats_st *espNowFloodingMesh_get_tmt_stats_ptr(void) { 165 | return &telemetry_stats; 166 | } 167 | 168 | 169 | // Telemetry related processing 170 | 171 | #ifdef ENABLE_TELEMETRY 172 | 173 | struct telemetry_db_item tdb[TELEMETRY_STATS_SIZE]; 174 | 175 | struct telemetry_db_item *espNowFloodingMesh_get_tdb_ptr(void) { 176 | return tdb; 177 | } 178 | 179 | void espNowFloodingMesh_telemetry_reset_tdb(void) { 180 | memset(tdb, 0, sizeof(tdb)); 181 | } 182 | 183 | // search for mac address in telemetry, return -1 if not found 184 | // index returned if found 185 | int16_t telemetry_get_tdb_idx_by_mac(const uint8_t *mac_addr) { 186 | int16_t idx = 0; 187 | bool found = false; 188 | while ( idx < TELEMETRY_STATS_SIZE && !found ) { 189 | if ( tdb[idx].mac_addr[0] == mac_addr[0] 190 | && tdb[idx].mac_addr[1] == mac_addr[1] 191 | && tdb[idx].mac_addr[2] == mac_addr[2] 192 | && tdb[idx].mac_addr[3] == mac_addr[3] 193 | && tdb[idx].mac_addr[4] == mac_addr[4] 194 | && tdb[idx].mac_addr[5] == mac_addr[5] ) { 195 | return idx; 196 | } 197 | idx++; 198 | } 199 | // not found 200 | return -1; 201 | } 202 | 203 | int16_t telemetry_get_tdb_slot(const uint8_t *mac_addr) { 204 | int16_t idx = telemetry_get_tdb_idx_by_mac(mac_addr); 205 | int16_t lidx = 0; // oldest lastseen index 206 | if (idx == -1) { 207 | // not found, let's find free spot or the oldest one 208 | bool found = false; 209 | uint32_t ts = 0; 210 | idx = 0; 211 | while ( idx < TELEMETRY_STATS_SIZE && !found ) { 212 | if ( tdb[idx].mac_addr[0] == 0 213 | && tdb[idx].mac_addr[1] == 0 214 | && tdb[idx].mac_addr[2] == 0 215 | && tdb[idx].mac_addr[3] == 0 216 | && tdb[idx].mac_addr[4] == 0 217 | && tdb[idx].mac_addr[5] == 0 ) { 218 | // found empty spot 219 | found = true; 220 | break; 221 | } 222 | if (ts == 0 || tdb[idx].lastseen < ts) { ts = tdb[idx].lastseen; lidx = idx; } 223 | idx++; 224 | } 225 | if (!found) { idx = lidx; } // replace the oldest one 226 | // copy mac addr 227 | for (int i=0; i<6; i++) { 228 | tdb[idx].mac_addr[i] = mac_addr[i]; 229 | } 230 | } 231 | return idx; 232 | } 233 | 234 | #endif 235 | 236 | 237 | struct requestReplyDbItem { 238 | void (*cb)(const uint8_t *, int); 239 | uint32_t messageIdentifierCode; 240 | time_t time; 241 | uint8_t ttl; 242 | }; 243 | 244 | class RequestReplyDataBase { 245 | public: 246 | RequestReplyDataBase() { 247 | index = 0; 248 | memset(db, 0, sizeof(db)); 249 | c = 1; 250 | mac = WiFi.macAddress(); 251 | muuid_base = calculateCRC(0, (const uint8_t*)mac.c_str(), 6); 252 | } 253 | ~RequestReplyDataBase(){} 254 | void add(uint32_t messageIdentifierCode, void (*f)(const uint8_t *, int)) { 255 | db[index].cb = f; 256 | db[index].messageIdentifierCode = messageIdentifierCode; 257 | db[index].time = espNowFloodingMesh_getRTCTime(); 258 | index++; 259 | if (index >= REQUEST_REPLY_DATA_BASE_SIZE) { 260 | index = 0; 261 | } 262 | } 263 | uint32_t calculateMessageIdentifier() { 264 | uint32_t ret = muuid_base; 265 | #ifdef ESP32 266 | ret = ret<<8 | (esp_random()&0xff); 267 | #else 268 | // ret = ret<<8 | (random(0, 0xff)&0xff); 269 | ret = ret<<8 | (secureRandom(0, 0xff) & 0xff); 270 | #endif 271 | ret = ret<<8 | c; 272 | c++; 273 | if (ret == 0) { ret = 1; } //messageIdentifier is never zero 274 | return ret; 275 | } 276 | 277 | const struct requestReplyDbItem* getCallback(uint32_t messageIdentifierCode) { 278 | time_t currentTime = espNowFloodingMesh_getRTCTime(); 279 | for (int i = 0; i < REQUEST_REPLY_DATA_BASE_SIZE; i++) { 280 | if (db[i].messageIdentifierCode == messageIdentifierCode) { 281 | if (compareTime(currentTime, db[i].time, MAX_ALLOWED_TIME_DIFFERENCE_IN_MESSAGES)) { 282 | if ( db[i].cb != NULL) { 283 | return &db[i]; 284 | } 285 | } 286 | } 287 | } 288 | return NULL; 289 | } 290 | 291 | void removeItem() { //Cleaning db --> Remove the oldest item 292 | memset(&db[index], 0, sizeof(struct requestReplyDbItem)); 293 | index++; 294 | if (index >= REQUEST_REPLY_DATA_BASE_SIZE) { 295 | index = 0; 296 | } 297 | } 298 | private: 299 | struct requestReplyDbItem db[REQUEST_REPLY_DATA_BASE_SIZE]; 300 | int index; 301 | uint8_t c; 302 | String mac; 303 | uint32_t muuid_base; 304 | }; 305 | 306 | RequestReplyDataBase requestReplyDB; 307 | 308 | class RejectedMessageDB { 309 | public: 310 | ~RejectedMessageDB() {} 311 | RejectedMessageDB() { 312 | memset(rejectedMsgList, 0, sizeof(rejectedMsgList)); 313 | memset(ttlList, 0, sizeof(ttlList)); 314 | index = 0; 315 | } 316 | 317 | void removeItem() { //Cleaning db --> Remove the oldest item 318 | rejectedMsgList[index] = 0; 319 | ttlList[index] = 0; 320 | index++; 321 | if (index >= REJECTED_LIST_SIZE) { 322 | index = 0; 323 | } 324 | } 325 | 326 | void addMessageToHandledList(struct meshFrame *m) { 327 | uint16_t crc = m->unencrypted.crc16; 328 | for (int i=0; iunencrypted.ttl) { 331 | ttlList[i] = m->unencrypted.ttl; 332 | } 333 | return; 334 | } 335 | } 336 | rejectedMsgList[index] = crc; 337 | ttlList[index] = m->unencrypted.ttl; 338 | 339 | index++; 340 | if (index >= REJECTED_LIST_SIZE) { 341 | index = 0; 342 | } 343 | } 344 | 345 | int isMessageInHandledList(struct meshFrame *m) { 346 | bool forwardNeeded = false; 347 | bool handled = false; 348 | uint16_t crc = m->unencrypted.crc16; 349 | for (int i=0; iunencrypted.ttl) { 353 | forwardNeeded = true; 354 | } 355 | break; 356 | } 357 | } 358 | if (forwardNeeded) return 2; 359 | if (handled) return 1; 360 | return 0; 361 | } 362 | private: 363 | uint16_t rejectedMsgList[REJECTED_LIST_SIZE]; 364 | uint8_t ttlList[REJECTED_LIST_SIZE]; 365 | int index; 366 | }; 367 | RejectedMessageDB rejectedMessageDB; 368 | 369 | 370 | void espNowFloodingMesh_RecvCB(void (*callback)(const uint8_t *, int, uint32_t)) { 371 | espNowFloodingMesh_receive_cb = callback; 372 | } 373 | 374 | void espNowFloodingMesh_delay(unsigned long tm) { 375 | // should be avoided or rewritten 376 | for (unsigned int i=0; i<(tm/10); i++) { 377 | espNowFloodingMesh_loop(); 378 | delay(10); 379 | } 380 | } 381 | 382 | void espNowFloodingMesh_loop() { 383 | 384 | if (isespNowFloodingMeshInitialized == false) { 385 | yield(); 386 | return; 387 | } 388 | 389 | uint32_t now = millis(); 390 | 391 | if (masterFlag) { 392 | static unsigned long start = 0; 393 | unsigned long elapsed = now - start; 394 | if ( elapsed >= RESEND_SYNC_TIME_MS ) { // 10s 395 | start = now; 396 | #ifdef DEBUG_PRINTS 397 | Serial.println("Send time sync message!!"); 398 | #endif 399 | print(3,"Send time sync message."); 400 | sendMsg(NULL, 0, syncTTL, SYNC_TIME_MSG); 401 | } 402 | } 403 | // Clean database 404 | static unsigned long dbtm = millis(); 405 | unsigned long elapsed = now - dbtm; 406 | if ( elapsed >= 500 ) { 407 | dbtm = now; 408 | requestReplyDB.removeItem(); 409 | rejectedMessageDB.removeItem(); 410 | } 411 | 412 | if (led_blink_mode == LED_BLINK_RX_MODE && led_is_on == true && recv_packet_ts < now - LED_BLINK_TIMEOUT_MS) { 413 | led_is_on = false; 414 | digitalWrite(led_pin, HIGH); // off 415 | } 416 | if (led_blink_mode == LED_BLINK_TX_MODE && led_is_on == true && tx_packet_ts < now - LED_BLINK_TIMEOUT_MS) { 417 | led_is_on = false; 418 | digitalWrite(led_pin, HIGH); // off 419 | } 420 | 421 | yield(); 422 | } 423 | 424 | void espNowFloodingMesh_setToMasterRole(bool master, unsigned char ttl) { 425 | masterFlag = master; 426 | syncTTL = ttl; 427 | } 428 | 429 | uint16_t calculateCRC(int c, const unsigned char*b,int len) { 430 | #ifdef ESP32JJJ 431 | return crc16_le(0, b, len); 432 | #else 433 | // Copied from https://www.lammertbies.nl/forum/viewtopic.php?t=1528 434 | uint16_t crc = 0xFFFF; 435 | uint8_t i; 436 | if (len) do { 437 | crc ^= *b++; 438 | for (i=0; i<8; i++) { 439 | if (crc & 1) crc = (crc >> 1) ^ 0x8408; 440 | else crc >>= 1; 441 | } 442 | } while (--len); 443 | return(~crc); 444 | #endif 445 | } 446 | 447 | uint16_t calculateCRC(struct meshFrame *m){ 448 | //uint16_t crc = m->encrypted.header.crc16; 449 | //m->encrypted.header.crc16 = 0; 450 | int size = m->encrypted.header.length + sizeof(m->encrypted.header); 451 | uint16_t ret = calculateCRC(0, (const unsigned char*)m + SECRED_PART_OFFSET, size); 452 | //m->encrypted.header.crc16 = crc; 453 | return ret; 454 | } 455 | 456 | void hexDump(const uint8_t*b, int len){ 457 | //#ifdef DEBUG_PRINTS 458 | Serial.println(); 459 | for (int i=0; i < len; i = i + 16) { 460 | Serial.print(" "); 461 | for(int x=0; x<16 && (x+i) < len; x++) { 462 | if(b[i+x]<=0xf) Serial.print("0"); 463 | Serial.print(b[i+x],HEX); 464 | Serial.print(" "); 465 | } 466 | Serial.print(" "); 467 | for(int x=0; x<16 && (x+i) < len; x++) { 468 | if (b[i+x]<=32||b[i+x] >= 126) { 469 | Serial.print("."); 470 | } else Serial.print((char)b[i+x]); 471 | } 472 | Serial.print("\n"); 473 | } 474 | Serial.print(" Length: "); 475 | Serial.println(len); 476 | // #endif 477 | } 478 | 479 | #ifdef ESP32 480 | void espNowFloodingMesh_setRTCTime(time_t time) { 481 | struct timeval now = { .tv_sec = time }; 482 | settimeofday(&now, NULL); 483 | if(masterFlag){ 484 | print(3, "Send time sync"); 485 | sendMsg(NULL, 0, syncTTL, SYNC_TIME_MSG); 486 | } 487 | } 488 | time_t espNowFloodingMesh_getRTCTime() { 489 | return time(NULL); 490 | } 491 | #else 492 | long long rtcFixValue = 0; 493 | void espNowFloodingMesh_setRTCTime(time_t t) { 494 | long long newTime = t; 495 | long long currentTime = time(NULL); 496 | rtcFixValue = newTime - currentTime; 497 | 498 | if (masterFlag) { 499 | print(3, "Send time sync"); 500 | sendMsg(NULL, 0, syncTTL, SYNC_TIME_MSG); 501 | } 502 | } 503 | time_t espNowFloodingMesh_getRTCTime() { 504 | long long currentTime = time(NULL); 505 | long long fixedTime = currentTime + rtcFixValue; 506 | return fixedTime; 507 | } 508 | #endif 509 | 510 | bool compareTime(time_t current, time_t received, time_t maxDifference) { 511 | if (timeStampCheckDisabled) { 512 | return true; 513 | } 514 | 515 | if (current == received) return true; 516 | if (current < received) { 517 | return ((received-current) <= maxDifference); 518 | } else { 519 | return ((current-received) <= maxDifference); 520 | } 521 | return false; 522 | } 523 | 524 | #ifdef USE_RAW_801_11 525 | void msg_recv_cb(const uint8_t *data, int len, uint8_t rssi) 526 | #else 527 | void msg_recv_cb(const uint8_t *data, int len, const uint8_t *mac_addr) 528 | #endif 529 | { 530 | // Serial.println("."); // RECEIVE PACKET 531 | #ifdef DEBUG_PRINTS 532 | char macStr[18]; 533 | snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x", 534 | mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); 535 | Serial.print("Recv from: "); Serial.println(macStr); 536 | Serial.print("REC[RAW]:"); 537 | hexDump((uint8_t*)data, len); 538 | #endif 539 | 540 | recv_packet_ts = millis(); 541 | 542 | if (led_blink_mode == LED_BLINK_RX_MODE) { 543 | digitalWrite(led_pin, LOW); // turn on 544 | led_is_on = true; 545 | } 546 | 547 | time_t currentTime = espNowFloodingMesh_getRTCTime(); 548 | 549 | #ifdef ENABLE_TELEMETRY 550 | int16_t tdb_idx = telemetry_get_tdb_slot(mac_addr); 551 | tdb[tdb_idx].msg_cnt++; 552 | tdb[tdb_idx].lastseen = currentTime & 0xFFFFFFFF; // truncated real timestamp 553 | telemetry_stats.received_pkt++; 554 | #endif 555 | 556 | struct meshFrame m; 557 | m.unencrypted.set(data); 558 | 559 | if ( (unsigned int) myBsid != m.unencrypted.getBsid() ) { 560 | // Serial.println(myBsid, HEX); 561 | // Serial.println(m.unencrypted.getBsid(), HEX); 562 | return; 563 | } 564 | if ( (unsigned int) len >= sizeof(struct meshFrame) ) return; 565 | 566 | int messageStatus = rejectedMessageDB.isMessageInHandledList(&m); 567 | if ( messageStatus != 0 ) { 568 | // Message is already handled... No need to forward 569 | #ifdef ENABLE_TELEMETRY 570 | tdb[tdb_idx].dup_msg_cnt++; 571 | //Serial.println("D"); 572 | #endif 573 | telemetry_stats.dup_pkt++; 574 | return; 575 | } 576 | rejectedMessageDB.addMessageToHandledList(&m); 577 | 578 | //memset(&m,0,sizeof(m)); 579 | decrypt((const uint8_t*)data, &m, len); 580 | #ifdef DEBUG_PRINTS 581 | Serial.print("REC:"); 582 | hexDump((uint8_t*)&m, m.encrypted.header.length + sizeof(m.encrypted.header) + 5 ); 583 | #endif 584 | 585 | if (!(m.encrypted.header.msgId == USER_MSG 586 | || m.encrypted.header.msgId == SYNC_TIME_MSG 587 | || m.encrypted.header.msgId == INSTANT_TIME_SYNC_REQ 588 | || m.encrypted.header.msgId == INSTANT_TIME_SYNC_REQ_ANNONCE 589 | || m.encrypted.header.msgId == USER_REQUIRE_RESPONSE_MSG 590 | || m.encrypted.header.msgId == USER_REQUIRE_REPLY_MSG)) { 591 | //Quick wilter; 592 | return; 593 | } 594 | 595 | if (m.encrypted.header.length >= 0 && m.encrypted.header.length < (sizeof(m.encrypted.data) ) ) { 596 | uint16_t crc = m.unencrypted.crc16; 597 | uint16_t crc16 = calculateCRC(&m); 598 | 599 | #ifdef DEBUG_PRINTS 600 | int messageLengtWithHeader = m.encrypted.header.length + sizeof(struct header); 601 | Serial.print("REC HEADER:"); 602 | hexDump((uint8_t*)&m, messageLengtWithHeader); 603 | #endif 604 | 605 | bool messageTimeOk = true; 606 | 607 | if (crc16 == crc) { 608 | 609 | if (!compareTime(currentTime, m.encrypted.header.time, MAX_ALLOWED_TIME_DIFFERENCE_IN_MESSAGES)) { 610 | messageTimeOk = false; 611 | print(1,"Received message with invalid time stamp."); 612 | // Serial.print("CurrentTime:");Serial.println(currentTime); 613 | // Serial.print("ReceivedTime:");Serial.println(m.encrypted.header.time); 614 | // shell we syncronize to it ? what about replay attack ? 615 | } 616 | 617 | bool ok = false; 618 | if (messageStatus == 0) { //if messageStatus==0 --> message is not handled yet. 619 | if (espNowFloodingMesh_receive_cb) { 620 | if ( m.encrypted.header.msgId == USER_MSG) { 621 | if (messageTimeOk) { 622 | // shell we rebroadcast message first ? (to reduce latency?) 623 | espNowFloodingMesh_receive_cb(m.encrypted.data, m.encrypted.header.length, m.encrypted.header.p1); 624 | ok = true; 625 | } else { 626 | #ifdef DEBUG_PRINTS 627 | Serial.print("Reject message because of time difference:"); 628 | Serial.print(currentTime); 629 | Serial.print(" "); 630 | Serial.println(m.encrypted.header.time); 631 | hexDump((uint8_t*)&m, messageLengtWithHeader); 632 | #endif 633 | } 634 | } 635 | 636 | if (m.encrypted.header.msgId == USER_REQUIRE_REPLY_MSG) { // ACK received 637 | if (messageTimeOk) { 638 | const struct requestReplyDbItem* d = requestReplyDB.getCallback(m.encrypted.header.p1); 639 | if (d != NULL) { 640 | d->cb(m.encrypted.data, m.encrypted.header.length); 641 | } else { 642 | espNowFloodingMesh_receive_cb(m.encrypted.data, m.encrypted.header.length, m.encrypted.header.p1); 643 | } 644 | ok = true; 645 | } else { 646 | #ifdef DEBUG_PRINTS 647 | Serial.print("Reject message because of time difference:");Serial.print(currentTime);Serial.print(" ");Serial.println(m.encrypted.header.time); 648 | hexDump((uint8_t*)&m, messageLengtWithHeader); 649 | #endif 650 | print(1,"ACK - Message rejected because of time difference."); 651 | } 652 | } 653 | 654 | if (m.encrypted.header.msgId == USER_REQUIRE_RESPONSE_MSG) { 655 | if (messageTimeOk) { 656 | espNowFloodingMesh_receive_cb(m.encrypted.data, m.encrypted.header.length, m.encrypted.header.p1); 657 | ok = true; 658 | } else { 659 | #ifdef DEBUG_PRINTS 660 | Serial.print("Reject message because of time difference:");Serial.print(currentTime);Serial.print(" ");Serial.println(m.encrypted.header.time); 661 | hexDump((uint8_t*)&m, messageLengtWithHeader); 662 | #endif 663 | print(1,"MSG REQUIRE RESPONSE - Message rejected because of time difference."); 664 | } 665 | } 666 | } 667 | 668 | if ( m.encrypted.header.msgId == INSTANT_TIME_SYNC_REQ ) { 669 | // ok = true; // we do not forward time sync messages -- only direct nodes can send time sync response 670 | if (masterFlag) { 671 | #ifdef DEBUG_PRINTS 672 | Serial.println("Send time sync message!! (Requested)"); 673 | #endif 674 | sendMsg(NULL, 0, 0, SYNC_TIME_MSG); // only for the direct nodes 675 | print(3,"Master - send time sync message (Requested)"); 676 | } else { 677 | if (syncronized) { 678 | sendMsg(NULL, 0, 0, SYNC_TIME_MSG); // only for the direct nodes 679 | print(3,"Send time sync message by node directly (Requested)"); 680 | } else { 681 | ok = true; // let's forward sync time request if we are not in sync 682 | } 683 | } 684 | } 685 | // timesync request with annoncement 686 | if ( m.encrypted.header.msgId == INSTANT_TIME_SYNC_REQ_ANNONCE ) { 687 | ok = true; // should be forwarded 688 | if (masterFlag) { 689 | #ifdef DEBUG_PRINTS 690 | Serial.println("Annonce - Send time sync message!! (Requested)"); 691 | #endif 692 | sendMsg(NULL, 0, 0, SYNC_TIME_MSG); // only for the direct nodes 693 | print(3,"Annonce + Master - send time sync message (Requested)"); 694 | } else { 695 | if (syncronized) { 696 | sendMsg(NULL, 0, 0, SYNC_TIME_MSG); // only for the direct nodes 697 | print(3,"Annonce + Send time sync message by node directly (Requested)"); 698 | } 699 | } 700 | if (espNowFloodingMesh_receive_cb) { 701 | espNowFloodingMesh_receive_cb(m.encrypted.data, m.encrypted.header.length, 0); 702 | } 703 | } 704 | 705 | if ( m.encrypted.header.msgId == SYNC_TIME_MSG ) { 706 | if (masterFlag) { 707 | //only slaves can be syncronized 708 | return; 709 | } 710 | static time_t last_time_sync = 0; 711 | #ifdef DEBUG_PRINTS 712 | Serial.print("Last sync time:"); Serial.println(last_time_sync); 713 | Serial.print("Sync time in message:"); Serial.println(m.encrypted.header.time); 714 | #endif 715 | 716 | if (last_time_sync 0 && batteryNode == false) { 739 | forwardMsg(data, len); 740 | } 741 | } else { 742 | #ifdef DEBUG_PRINTS 743 | Serial.print("#CRC: ");Serial.print(crc16);Serial.print(" "),Serial.println(crc); 744 | for (int i=0;iencrypted)) { 850 | memcpy((uint8_t*)m+i+SECRED_PART_OFFSET, to, 16); 851 | } 852 | } 853 | return 0; 854 | } 855 | 856 | int encrypt(struct meshFrame *m) { 857 | int size = ((m->encrypted.header.length + sizeof(m->encrypted.header))/16)*16+16; 858 | 859 | unsigned char iv[16]; 860 | memcpy(iv,ivKey,sizeof(iv)); 861 | uint8_t to[2*16]; 862 | 863 | for(int i=0;iencrypted, key, 128, iv); 881 | break; 882 | #endif 883 | #endif 884 | memcpy((uint8_t*)m+i+SECRED_PART_OFFSET, to, 16); 885 | } 886 | /* 887 | for(int i=m->encrypted.header.length + sizeof(m->encrypted.header)+1;iencrypted.header)[i]=esp_random(); 890 | #else 891 | ((unsigned char*)&m->encrypted.header)[i]=random(0, 255); 892 | #endif 893 | }*/ 894 | 895 | return size + SECRED_PART_OFFSET; 896 | } 897 | 898 | bool forwardMsg(const uint8_t *data, int len) { 899 | struct meshFrame m; 900 | memcpy(&m, data,len); 901 | 902 | if (m.unencrypted.ttl == 0) { 903 | #ifdef DEBUG_PRINTS 904 | Serial.print("FORWARD: TTL=0\n"); 905 | #endif 906 | // telemetry_stats.ttl0_pkt++; 907 | return false; 908 | } 909 | 910 | m.unencrypted.ttl = m.unencrypted.ttl-1; 911 | 912 | #ifdef DEBUG_PRINTS 913 | Serial.print("FORWARD:"); 914 | hexDump((const uint8_t*)data, len); 915 | #endif 916 | 917 | #ifdef USE_RAW_801_11 918 | wifi_802_11_send((uint8_t*)(&m), len); 919 | #else 920 | espnowBroadcast_send((uint8_t*)(&m), len); 921 | #endif 922 | telemetry_stats.fwd_pkt++; 923 | return true; 924 | } 925 | 926 | uint32_t sendMsgId(uint8_t* msg, int size, uint32_t umsgid, int ttl, int msgId, void *ptr) { 927 | uint32_t ret = 0; 928 | if ( (unsigned int) size >= sizeof(struct mesh_secred_part) ) { 929 | #ifdef DEBUG_PRINTS 930 | Serial.println("espNowFloodingMesh_send: Invalid size"); 931 | #endif 932 | return false; 933 | } 934 | 935 | static struct meshFrame m; 936 | memset(&m, 0x00, sizeof(struct meshFrame)); 937 | m.encrypted.header.length = size; 938 | m.unencrypted.crc16 = 0; 939 | m.encrypted.header.msgId = msgId; // message type 940 | m.unencrypted.ttl= ttl; 941 | m.unencrypted.setBsid(myBsid); 942 | m.encrypted.header.p1 = umsgid; 943 | m.encrypted.header.time = espNowFloodingMesh_getRTCTime(); 944 | 945 | if ( msg != NULL ) { 946 | memcpy(m.encrypted.data, msg, size); 947 | } 948 | 949 | if ( msgId == USER_REQUIRE_RESPONSE_MSG ) { 950 | 951 | ret = m.encrypted.header.p1; 952 | requestReplyDB.add(m.encrypted.header.p1, (void (*)(const uint8_t*, int))ptr); 953 | //Serial.print("Send request with "); Serial.println(m.encrypted.header.p1); 954 | } if ( msgId == USER_REQUIRE_REPLY_MSG && ptr != NULL ) { 955 | m.encrypted.header.p1 = *((uint32_t*)ptr); 956 | } 957 | 958 | m.unencrypted.crc16 = calculateCRC(&m); 959 | #ifdef DEBUG_PRINTS 960 | Serial.print("Send0:"); 961 | hexDump((const uint8_t*)&m, size+20); 962 | #endif 963 | rejectedMessageDB.addMessageToHandledList(&m); 964 | 965 | int sendSize = encrypt(&m); 966 | 967 | /* 968 | struct meshFrame mm; 969 | Serial.print("--->:"); 970 | decrypt((const uint8_t*)&m, &mm, sendSize); 971 | Serial.print("--->:"); 972 | hexDump((const uint8_t*)&mm, size+20); 973 | Serial.print("--->:"); 974 | */ 975 | 976 | #ifdef DEBUG_PRINTS 977 | Serial.print("Send[RAW]:"); 978 | hexDump((const uint8_t*)&m, sendSize); 979 | #endif 980 | // Serial.println("#"); // SEND PACKET 981 | 982 | #ifdef USE_RAW_801_11 983 | wifi_802_11_send((uint8_t*)&m, sendSize); 984 | #else 985 | espnowBroadcast_send((uint8_t*)&m, sendSize); 986 | #endif 987 | telemetry_stats.sent_pkt++; 988 | 989 | if (led_blink_mode == LED_BLINK_TX_MODE) { 990 | digitalWrite(led_pin, LOW); // turn on 991 | led_is_on = true; 992 | } 993 | 994 | tx_packet_ts = millis(); 995 | 996 | return ret; 997 | } 998 | 999 | uint32_t sendMsg(uint8_t* msg, int size, int ttl, int msgId, void *ptr) { 1000 | uint32_t umgsid = requestReplyDB.calculateMessageIdentifier(); 1001 | return sendMsgId(msg, size, umgsid, ttl, msgId, ptr); 1002 | } 1003 | 1004 | void espNowFloodingMesh_send(uint8_t* msg, int size, int ttl) { 1005 | sendMsg(msg, size, ttl, USER_MSG); 1006 | } 1007 | 1008 | void espNowFloodingMesh_sendReply(uint8_t* msg, int size, int ttl, uint32_t replyIdentifier) { 1009 | sendMsg(msg, size, ttl, USER_REQUIRE_REPLY_MSG, (void*)&replyIdentifier); 1010 | } 1011 | 1012 | uint32_t espNowFloodingMesh_sendAndHandleReply(uint8_t* msg, int size, int ttl, void (*f)(const uint8_t *, int)) { 1013 | return sendMsg(msg, size, ttl, USER_REQUIRE_RESPONSE_MSG, (void*)f); 1014 | } 1015 | 1016 | uint32_t espNowFloodingMesh_sendAndHandleReplyUmid(uint8_t* msg, int size, uint32_t umsgid, int ttl, void (*f)(const uint8_t *, int)) { 1017 | return sendMsgId(msg, size, umsgid, ttl, USER_REQUIRE_RESPONSE_MSG, (void*)f); 1018 | } 1019 | 1020 | bool espNowFloodingMesh_sendAndWaitReply(uint8_t* msg, int size, int ttl, int tryCount, void (*f)(const uint8_t *, int), int timeoutMs, int expectedCountOfReplies, uint16_t backoffMs){ 1021 | static uint16_t replyCnt = 0; 1022 | replyCnt = 0; 1023 | static void (*callback)(const uint8_t *, int); 1024 | callback = f; 1025 | 1026 | for (int i=0; i (unsigned int) timeoutMs ) { 1043 | //timeout 1044 | if (i < 10) { timeoutMs += backoffMs; } 1045 | print(0, "Timeout: waiting replies"); 1046 | break; 1047 | } 1048 | } 1049 | } 1050 | return false; 1051 | } 1052 | 1053 | bool espNowFloodingMesh_syncTimeAndWait(unsigned long timeoutMs, int tryCount, uint16_t backoffMs) { 1054 | if (masterFlag || timeStampCheckDisabled) return true; 1055 | syncronized = false; 1056 | for (int i=0; i timeoutMs) { 1067 | if (i < 10) { timeoutMs += backoffMs; } 1068 | break; 1069 | } 1070 | } 1071 | } 1072 | return false; 1073 | } 1074 | 1075 | bool espNowFloodingMesh_syncTimeAnnonceAndWait(uint8_t* msg, int size, unsigned long timeoutMs, int tryCount, uint16_t backoffMs) { 1076 | if (masterFlag || timeStampCheckDisabled) return true; 1077 | syncronized = false; 1078 | for (int i=0; i timeoutMs) { 1089 | if (i < 10) { timeoutMs += backoffMs; } 1090 | break; 1091 | } 1092 | } 1093 | } 1094 | return false; 1095 | } 1096 | -------------------------------------------------------------------------------- /EspNowFloodingMesh.h: -------------------------------------------------------------------------------- 1 | #ifndef ESP_NOBRADCAST_H 2 | #define ESP_NOBRADCAST_H 3 | 4 | #define DEFAULT_TIMEOUT_MS 3000 5 | #define DEFAULT_TRY_CNT 3 6 | //#define USE_RAW_801_11 7 | 8 | #define ENABLE_TELEMETRY 9 | #define TELEMETRY_STATS_SIZE 10 10 | 11 | #define LED_BLINK_RX_MODE 1 12 | #define LED_BLINK_TX_MODE 2 13 | #define LED_BLINK_TIMEOUT_MS 40 14 | 15 | #ifndef USE_RAW_801_11 16 | #ifdef ESP32 17 | // #include 18 | #else 19 | // #include 20 | #endif 21 | #endif 22 | 23 | //#define DISABLE_CRYPTING //send messages as plain text 24 | //#define DEBUG_PRINTS 25 | 26 | #define MAX_ALLOWED_TIME_DIFFERENCE_IN_MESSAGES 3 //if message time differens more than this from RTC, reject message 27 | 28 | #ifndef USE_RAW_801_11 29 | void espNowFloodingMesh_begin(int channel, int bsid, bool disconnect_wifi = true); 30 | #else 31 | void espNowFloodingMesh_begin(int channel, char bsId[6], bool disconnect_wifi = true); 32 | #endif 33 | 34 | void espNowFloodingMesh_end(); 35 | 36 | void espNowFloodingMesh_enableBlink(int8_t pin, uint8_t mode = 1); 37 | 38 | void espNowFloodingMesh_setChannel(int channel); 39 | 40 | void espNowFloodingMesh_setToMasterRole(bool master=true, unsigned char ttl=0 /*ttl for sync messages*/); 41 | void espNowFloodingMesh_setToBatteryNode(bool isBatteryNode=true); 42 | 43 | void espNowFloodingMesh_RecvCB(void (*callback)(const uint8_t *, int, uint32_t)); 44 | void espNowFloodingMesh_send(uint8_t* msg, int size, int ttl=0); //Max message length is 236byte 45 | void espNowFloodingMesh_secredkey(const unsigned char key[16]); 46 | void espNowFloodingMesh_setAesInitializationVector(const unsigned char iv[16]); 47 | 48 | void espNowFloodingMesh_ErrorDebugCB(void (*callback)(int,const char *)); 49 | 50 | void espNowFloodingMesh_disableTimeDifferenceCheck(bool disable=true); //Decreases security, but you can communicate without master and without timesync 51 | 52 | uint32_t espNowFloodingMesh_sendAndHandleReply(uint8_t* msg, int size, int ttl, void (*f)(const uint8_t *, int)); //Max message length is 236byte 53 | uint32_t espNowFloodingMesh_sendAndHandleReplyUmid(uint8_t* msg, int size, uint32_t umsgid, int ttl, void (*f)(const uint8_t *, int)); //Max message length is 236byte 54 | 55 | // Run this only in Mainloop!!! 56 | // Max message length is 236byte 57 | bool espNowFloodingMesh_sendAndWaitReply(uint8_t* msg, int size, int ttl, int tryCount=10, void (*f)(const uint8_t *, int)=NULL, int timeoutMs=DEFAULT_TIMEOUT_MS, int expectedCountOfReplies=1, uint16_t backoffMs=0); 58 | bool espNowFloodingMesh_syncTimeAndWait(unsigned long timeoutMs=DEFAULT_TIMEOUT_MS, int tryCount=DEFAULT_TRY_CNT, uint16_t backoffMs=0); 59 | // the same but with the message annoncement (can be device name, config etc.) 60 | bool espNowFloodingMesh_syncTimeAnnonceAndWait(uint8_t* msg, int size, unsigned long timeoutMs=DEFAULT_TIMEOUT_MS, int tryCount=DEFAULT_TRY_CNT, uint16_t backoffMs=0); 61 | 62 | void espNowFloodingMesh_sendReply(uint8_t* msg, int size, int ttl, uint32_t replyIdentifier); 63 | 64 | void espNowFloodingMesh_loop(); 65 | 66 | void espNowFloodingMesh_delay(unsigned long tm); 67 | int espNowFloodingMesh_getTTL(); 68 | 69 | void espNowFloodingMesh_setRTCTime(time_t time); 70 | time_t espNowFloodingMesh_getRTCTime(); 71 | 72 | #pragma pack(push, 1) 73 | struct telemetry_stats_st { 74 | uint32_t received_pkt; 75 | uint32_t dup_pkt; 76 | uint32_t sent_pkt; 77 | uint32_t fwd_pkt; 78 | // uint32_t ttl0_pkt; 79 | }; 80 | #pragma pack(pop) 81 | 82 | telemetry_stats_st *espNowFloodingMesh_get_tmt_stats_ptr(void); 83 | 84 | #ifdef ENABLE_TELEMETRY 85 | 86 | #pragma pack(push, 1) 87 | struct telemetry_db_item { 88 | uint8_t mac_addr[6]; 89 | uint32_t lastseen; // truncated ts from time_t 90 | uint16_t msg_cnt; 91 | uint16_t dup_msg_cnt; 92 | }; 93 | #pragma pack(pop) 94 | 95 | struct telemetry_db_item *espNowFloodingMesh_get_tdb_ptr(void); 96 | void espNowFloodingMesh_telemetry_reset_tdb(void); 97 | #endif 98 | 99 | #endif 100 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ESPNOW flooding mesh library 2 | 3 | See example project: https://github.com/leodesigner/esp_mesh_pir_sensor 4 | 5 | ESPNOW flooding mesh library, version 2 6 | forked from https://github.com/arttupii/espNowFloodingMeshLibrary 7 | 8 | Features added/bugfixes since version 2: 9 | - a lot of bugfixes and improvements, battle tested with 15+ nodes 10 | - telemetry stats (optional) 11 | - time sync after bootup is now possible from any node, not only master 12 | - new INSTANT_TIME_SYNC_REQ_ANNONCE internal message for node annoncement 13 | - led blinking support on message receive 14 | - mesh begin can now run without WiFi disconnect (speeds up boot time, important for PIR sensor sleeping nodes) 15 | - new function for sending messages with specific message ID (future support for multi mqtt gataway setup) 16 | - message roundtrip reduced as much as possible to get more reponsive real-time system 17 | 18 | Features: 19 | - Maximum number of slave nodes: unlimited 20 | - Number of master nodes: 1 21 | - Master node sends time sync message every 10s to all nodes. (this synchronizes the clocks of the nodes) 22 | - a message cache. If a received packet is already found in the cache --> it will not be retransmitted or handled again 23 | - Every message has a time stamp. If the time stamp is too old (or from the future), the message will be rejected. 24 | - All messages are crypted (AES128) 25 | - Flooding mesh support 26 | - TTL support (time to life) 27 | - ESP32, ESP2866, ESP01 28 | - Battery node support (Battery nodes do not relay messages) 29 | - Request&Reply support 30 | - Each Nodes can communicate with each other 31 | - Ping about 40-60ms 32 | - Nearly instant connection after poweron 33 | - Retransmission support 34 | - Request/Reply support 35 | - Send and pray support (Send a message to all nodes without reply/ack) 36 | - Easy to configure (Set only the same bsid, iv and secred key to all nodes) 37 | - Works on esp-now broadcast 38 | - Arduino 39 | 40 | 41 | ## Flooding mesh network 42 | In this network example ttl must be >= 4 43 | ``` 44 | SlaveNode 45 | | 46 | | Message from master to BatteryNode 47 | | ---------------------------+ 48 | | ttl=4 | 49 | SlaveNode-------MasterNode-------------SlaveNode | 50 | | | | 51 | | | | 52 | | | | 53 | | | | 54 | SlaveNode | | 55 | | | | 56 | | | | 57 | | | +------------------------------------------------> 58 | | | ttl=3 ttl=2 ttl=1 59 | SlaveNode-------SlaveNode-------------SlaveNode-------SlaveNode-------------SlaveNode---------BatteryNode 60 | | | | 61 | | | | 62 | | | | 63 | | | | 64 | +-----------SlaveNode-----------------+ 65 | ``` 66 | ## Message headers 67 | ``` 68 | +---------------------------------------------------------------------------------------+ 69 | | AES128 Crypted header (Mesh-header part2) | 70 | | --------------------------------------------------------------------------------- | 71 | | |msgId | length | replyId | time stamp | data | | 72 | | --------------------------------------------------------------------------------- | 73 | | 1 byte 1 byte 4 byte 4-byte 230 | 74 | +---------------------------------------------------------------------------------------+ 75 | ^ 76 | \ 77 | +---------------------------------------------------------\-----------------------------+ 78 | | Mesh-header part 1 \ | 79 | | --------------------------------------------------------------------------------- | 80 | | |bsId | ttl | crc | AES128 crypted data | | 81 | | --------------------------------------------------------------------------------- | 82 | | 3 byte 1 byte 2 byte 240-byte | 83 | +---------------------------------------------------------------------------------------+ 84 | ^ 85 | \ 86 | +-------------------------------------------------------------------- \---------------+ 87 | | Espnow-header \ | 88 | | ------------------------------------------------------------------------------- | 89 | | | Element ID | Length | Organization Identifier | Type | Version | Body | | 90 | | ------------------------------------------------------------------------------- | 91 | | 1 byte 1 byte 3 bytes 1 byte 1 byte 0~250 bytes | 92 | | | 93 | +-------------------------------------------------------------------------------------+ 94 | ^ 95 | \ 96 | +---------------------------------------------------------------------\--------------------+ 97 | | \ | 98 | | ---------------------------------------------------------------------------------------- | 99 | | |MAC Header | Category Code | Organization Identifier | Vendor Specific Content | FCS | | 100 | | ---------------------------------------------------------------------------------------- | 101 | | 1 byte 3 bytes 7~255 bytes | 102 | +------------------------------------------------------------------------------------------+ 103 | ``` ´ 104 | 105 | ## Create master node: 106 | ```c++ 107 | #include 108 | 109 | #define ESP_NOW_CHANNEL 1 110 | //AES 128bit 111 | unsigned char secredKey[] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE, 0xFF}; 112 | 113 | void espNowFloodingMeshRecv(const uint8_t *data, int len){ 114 | if(len>0) { 115 | Serial.println((const char*)data); 116 | } 117 | } 118 | 119 | void setup() { 120 | Serial.begin(115200); 121 | 122 | espNowFloodingMesh_RecvCB(espNowFloodingMeshRecv); 123 | espNowFloodingMesh_secredkey(secredKey); 124 | espNowFloodingMesh_begin(ESP_NOW_CHANNEL); 125 | espNowFloodingMesh_setToMasterRole(true,3); //Set ttl to 3. TIME_SYNC message use this ttl 126 | espNowFloodingMesh_ErrorDebugCB([](int level, const char *str) { 127 | Serial.println(str); 128 | }); 129 | } 130 | 131 | void loop() { 132 | static unsigned long m = millis(); 133 | if(m+5000 145 | 146 | #define ESP_NOW_CHANNEL 1 147 | //AES 128bit 148 | unsigned char secredKey[] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF}; 149 | 150 | void espNowFloodingMeshRecv(const uint8_t *data, int len){ 151 | if(len>0) { 152 | Serial.println((const char*)data); 153 | } 154 | } 155 | 156 | void setup() { 157 | Serial.begin(115200); 158 | 159 | espNowFloodingMesh_RecvCB(espNowFloodingMeshRecv); 160 | espNowFloodingMesh_begin(ESP_NOW_CHANNEL); 161 | espNowFloodingMesh_secredkey(secredKey); 162 | 163 | //Ask instant sync from master. 164 | espNowFloodingMesh_requestInstantTimeSyncFromMaster(); 165 | espNowFloodingMesh_ErrorDebugCB([](int level, const char *str) { 166 | Serial.println(str); 167 | }); 168 | while(espNowFloodingMesh_isSyncedWithMaster()==false); 169 | } 170 | 171 | void loop() { 172 | static unsigned long m = millis(); 173 | if(m+5000 186 | #include 187 | #define ESP_NOW_CHANNEL 1 188 | //AES 128bit 189 | unsigned char secredKey[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; 190 | 191 | void espNowFloodingMeshRecv(const uint8_t *data, int len) { 192 | if (len > 0) { 193 | Serial.println((const char*)data); 194 | } 195 | } 196 | 197 | void setup() { 198 | Serial.begin(115200); 199 | //Set device in AP mode to begin with 200 | espNowFloodingMesh_RecvCB(espNowFloodingMeshRecv); 201 | espNowFloodingMesh_begin(ESP_NOW_CHANNEL); 202 | espNowFloodingMesh_secredkey(secredKey); 203 | espNowFloodingMesh_setToBatteryNode(); 204 | } 205 | 206 | void loop() { 207 | static unsigned long m = millis(); 208 | 209 | //Ask instant sync from master. 210 | espNowFloodingMesh_requestInstantTimeSyncFromMaster(); 211 | while (espNowFloodingMesh_isSyncedWithMaster() == false); 212 | char message[] = "SLAVE(12) HELLO MESSAGE"; 213 | espNowFloodingMesh_send((uint8_t*)message, sizeof(message), 0); //set ttl to 3 214 | espNowFloodingMesh_loop(); 215 | ESP.deepSleep(60000, WAKE_RF_DEFAULT); //Wakeup every minute 216 | } 217 | ``` 218 | ## Send message and get reply: 219 | Send "MARCO" to other nodes 220 | ```c++ 221 | 222 | #include 223 | 224 | #define ESP_NOW_CHANNEL 1 225 | //AES 128bit 226 | unsigned char secredKey[] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE, 0xFF}; 227 | 228 | void espNowFloodingMeshRecv(const uint8_t *data, int len, uint32_t replyPrt){ 229 | } 230 | 231 | void setup() { 232 | Serial.begin(115200); 233 | //Set device in AP mode to begin with 234 | espNowFloodingMesh_RecvCB(espNowFloodingMeshRecv); 235 | espNowFloodingMesh_secredkey(secredKey); 236 | espNowFloodingMesh_begin(ESP_NOW_CHANNEL); 237 | espNowFloodingMesh_setToMasterRole(true,3); //Set ttl to 3. 238 | espNowFloodingMesh_ErrorDebugCB([](int level, const char *str) { 239 | Serial.println(str); 240 | }); 241 | } 242 | 243 | void loop() { 244 | static unsigned long m = millis(); 245 | if(m+50000) { //Handle reply from other node 249 | Serial.print("Reply: "); //Prinst POLO. 250 | Serial.println((const char*)data); 251 | } 252 | }); 253 | m = millis(); 254 | } 255 | espNowFloodingMesh_loop(); 256 | delay(10); 257 | } 258 | ``` 259 | Answer to "MARCO" and send "POLO" 260 | ```c++ 261 | #include 262 | 263 | #define ESP_NOW_CHANNEL 1 264 | //AES 128bit 265 | unsigned char secredKey[] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE, 0xFF}; 266 | 267 | void espNowFloodingMeshRecv(const uint8_t *data, int len, uint32_t replyPrt){ 268 | if(len>0) { 269 | if(replyPrt) { //Reply asked. Send reply 270 | char m[]="POLO"; 271 | Serial.println((char*)data); //Prints MARCO 272 | espNowFloodingMesh_sendReply((uint8_t*)m, sizeof(m), 0, replyPrt); //Special function for reply messages. Only the sender gets this message. 273 | } else { 274 | //No reply asked... All others messages are handled in here. 275 | } 276 | } 277 | } 278 | 279 | void setup() { 280 | Serial.begin(115200); 281 | //Set device in AP mode to begin with 282 | espNowFloodingMesh_RecvCB(espNowFloodingMeshRecv); 283 | espNowFloodingMesh_secredkey(secredKey); 284 | espNowFloodingMesh_begin(ESP_NOW_CHANNEL); 285 | 286 | espNowFloodingMesh_requestInstantTimeSyncFromMaster(); 287 | espNowFloodingMesh_ErrorDebugCB([](int level, const char *str) { 288 | Serial.println(str); 289 | }); 290 | while (espNowFloodingMesh_isSyncedWithMaster() == false); 291 | } 292 | 293 | void loop() { 294 | espNowFloodingMesh_loop(); 295 | delay(10); 296 | } 297 | ``` 298 | -------------------------------------------------------------------------------- /espnowBroadcast.cpp: -------------------------------------------------------------------------------- 1 | //#define DEBUG_PRINTS 2 | 3 | #ifdef ESP32 4 | #include 5 | #include 6 | #else 7 | #include 8 | #include 9 | #include 10 | #define ESP_OK 0 11 | #endif 12 | #include "espnowBroadcast.h" 13 | 14 | const unsigned char broadcast_mac[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; 15 | bool init_done = false; 16 | void(*espnowCB)(const uint8_t *, int, const uint8_t *) = NULL; 17 | 18 | #ifdef ESP32 19 | void esp_msg_recv_cb(const uint8_t *mac_addr, const uint8_t *data, int len) 20 | #else 21 | void esp_msg_recv_cb(u8 *mac_addr, u8 *data, u8 len) 22 | #endif 23 | { 24 | #ifdef DEBUG_PRINTS 25 | char macStr[18]; 26 | snprintf(macStr, sizeof(macStr), "%02x:%02x:%02x:%02x:%02x:%02x", 27 | mac_addr[0], mac_addr[1], mac_addr[2], mac_addr[3], mac_addr[4], mac_addr[5]); 28 | Serial.print("Last Packet Recv from: "); Serial.println(macStr); 29 | #endif 30 | //Serial.print("."); 31 | if ( espnowCB != NULL ) { 32 | espnowCB(data, len, mac_addr); 33 | } 34 | } 35 | 36 | #ifdef DEBUG_PRINTS 37 | bool sending = false; 38 | long send_ts = 0; 39 | #endif 40 | 41 | #ifdef ESP32 42 | static void msg_send_cb(const uint8_t* mac, esp_now_send_status_t sendStatus) 43 | { 44 | #ifdef DEBUG_PRINTS 45 | Serial.print("^"); 46 | Serial.println(sendStatus); 47 | #endif 48 | } 49 | #else 50 | static void msg_send_cb(u8* mac_addr, u8 status) 51 | { 52 | #ifdef DEBUG_PRINTS 53 | sending = false; 54 | Serial.print("^"); 55 | Serial.print(status); 56 | Serial.print(" > elapsed: "); 57 | Serial.println(micros() - send_ts); 58 | #endif 59 | } 60 | #endif 61 | 62 | void espnowBroadcast_begin(int channel) { 63 | 64 | // takes too much time - now it's external 65 | //WiFi.mode(WIFI_STA); 66 | //WiFi.disconnect(); 67 | 68 | if (esp_now_init() != ESP_OK) { 69 | Serial.println("Error initializing ESP-NOW"); 70 | return; 71 | } 72 | 73 | // Set up callback 74 | esp_now_register_recv_cb(esp_msg_recv_cb); 75 | esp_now_register_send_cb(msg_send_cb); 76 | 77 | 78 | #ifdef ESP32 79 | static esp_now_peer_info_t slave; 80 | memset(&slave, 0, sizeof(slave)); 81 | for (int ii = 0; ii < 6; ++ii) { 82 | slave.peer_addr[ii] = (uint8_t)0xff; 83 | } 84 | slave.channel = channel; // pick a channel 85 | slave.encrypt = 0; // no encryption 86 | 87 | const esp_now_peer_info_t *peer = &slave; 88 | const uint8_t *peer_addr = slave.peer_addr; 89 | esp_now_add_peer(peer); 90 | #else 91 | esp_now_set_self_role(ESP_NOW_ROLE_SLAVE); 92 | esp_now_add_peer((u8*)broadcast_mac, ESP_NOW_ROLE_SLAVE, channel, NULL, 0); 93 | #endif 94 | init_done = true; 95 | 96 | } 97 | 98 | void espnowBroadcast_send(const uint8_t *d, int len){ 99 | if (init_done == false) { 100 | #ifdef DEBUG_PRINTS 101 | Serial.println("espnowBroadcast not initialized"); 102 | #endif 103 | return; 104 | } 105 | #ifdef ESP32 106 | esp_now_send(broadcast_mac, (uint8_t*)(d), len); 107 | #else 108 | #ifdef DEBUG_PRINTS 109 | //Serial.print("*"); 110 | if (sending) { 111 | Serial.print("Error - we did't receive sent callback!, last sent was: "); 112 | Serial.println(micros() - send_ts); 113 | //delay(3); 114 | } 115 | sending = true; 116 | send_ts = micros(); 117 | #endif 118 | int result = esp_now_send((u8*)broadcast_mac, (u8*)(d), len); 119 | 120 | if (result != ESP_OK) { 121 | #ifdef DEBUG_PRINTS 122 | Serial.print("Error sending the data: "); 123 | Serial.println(result); 124 | #endif 125 | } 126 | 127 | #endif 128 | } 129 | 130 | void espnowBroadcast_cb(void(*cb)(const uint8_t *, int, const uint8_t *)) { 131 | espnowCB = cb; 132 | } 133 | -------------------------------------------------------------------------------- /espnowBroadcast.h: -------------------------------------------------------------------------------- 1 | #ifndef espnowBroadcast_H 2 | #define espnowBroadcast_H 3 | #include 4 | 5 | void espnowBroadcast_begin(int channel); 6 | void espnowBroadcast_send(const uint8_t *d, int len); 7 | void espnowBroadcast_cb(void(*cb)(const uint8_t *, int, const uint8_t *)); 8 | 9 | #endif 10 | -------------------------------------------------------------------------------- /examples/Broadcast/espnowboradcast/espnowboradcast.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | void rec(const uint8_t *d, int l){ 4 | Serial.println((const char*)d); 5 | } 6 | void setup() { 7 | Serial.begin(115200); 8 | // put your setup code here, to run once: 9 | espnowBroadcast_begin(1); 10 | espnowBroadcast_cb(rec); 11 | } 12 | 13 | void loop() { 14 | // put your main code here, to run repeatedly: 15 | delay(1000); 16 | Serial.println("Send"); 17 | espnowBroadcast_send((const uint8_t*)"HELLO22", 6); 18 | } 19 | -------------------------------------------------------------------------------- /examples/askReply/master/master.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define ESP_NOW_CHANNEL 1 4 | //AES 128bit 5 | unsigned char secredKey[] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE, 0xFF}; 6 | 7 | void espNowFloodingMeshRecv(const uint8_t *data, int len, uint32_t replyPrt){ 8 | } 9 | 10 | void setup() { 11 | Serial.begin(115200); 12 | //Set device in AP mode to begin with 13 | espNowFloodingMesh_RecvCB(espNowFloodingMeshRecv); 14 | espNowFloodingMesh_secredkey(secredKey); 15 | espNowFloodingMesh_begin(ESP_NOW_CHANNEL); 16 | espNowFloodingMesh_setToMasterRole(true,3); //Set ttl to 3. 17 | } 18 | 19 | void loop() { 20 | static unsigned long m = millis(); 21 | if(m+50000) { 25 | Serial.print(">"); 26 | Serial.println((const char*)data); 27 | } 28 | }); 29 | m = millis(); 30 | } 31 | espNowFloodingMesh_loop(); 32 | delay(10); 33 | } 34 | -------------------------------------------------------------------------------- /examples/askReply/slave/slave.ino: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define ESP_NOW_CHANNEL 1 4 | //AES 128bit 5 | unsigned char secredKey[] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE, 0xFF}; 6 | 7 | void hexDump2(const uint8_t*b,int len){ 8 | Serial.println(); 9 | for(int i=0;i=126) { 19 | Serial.print("_"); 20 | } else Serial.print((char)b[i+x]); 21 | } 22 | Serial.print("\n"); 23 | } 24 | Serial.print(" Length: "); 25 | Serial.println(len); 26 | } 27 | 28 | void espNowFloodingMeshRecv(const uint8_t *data, int len, uint32_t replyPrt){ 29 | if(len>0) { 30 | if(replyPrt) { //Reply asked. Send reply 31 | char m[]="POLO"; 32 | Serial.println((char*)data); //Print MARCO 33 | Serial.println("POLO"); 34 | espNowFloodingMesh_sendReply((uint8_t*)m, sizeof(m), 0/*ttl*/, replyPrt); 35 | Serial.println(replyPrt); 36 | } else { 37 | hexDump2(data,len); 38 | } 39 | } 40 | } 41 | 42 | void setup() { 43 | Serial.begin(115200); 44 | //Set device in AP mode to begin with 45 | espNowFloodingMesh_RecvCB(espNowFloodingMeshRecv); 46 | espNowFloodingMesh_secredkey(secredKey); 47 | espNowFloodingMesh_begin(ESP_NOW_CHANNEL); 48 | 49 | espNowFloodingMesh_requestInstantTimeSyncFromMaster(); 50 | while (espNowFloodingMesh_isSyncedWithMaster() == false); 51 | } 52 | 53 | void loop() { 54 | espNowFloodingMesh_loop(); 55 | delay(10); 56 | 57 | static unsigned long m = millis(); 58 | if(m+5000 3 | 4 | #define ESP_NOW_CHANNEL 1 5 | //AES 128bit 6 | unsigned char secredKey[] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE, 0xFF}; 7 | 8 | void espNowFloodingMeshRecv(const uint8_t *data, int len, uint32_t replyPrt){ 9 | } 10 | 11 | void setup() { 12 | Serial.begin(115200); 13 | //Set device in AP mode to begin with 14 | espNowFloodingMesh_RecvCB(espNowFloodingMeshRecv); 15 | espNowFloodingMesh_secredkey(secredKey); 16 | espNowFloodingMesh_begin(ESP_NOW_CHANNEL); 17 | espNowFloodingMesh_setToMasterRole(true,3); //Set ttl to 3. 18 | } 19 | 20 | void loop() { 21 | static unsigned long m = millis(); 22 | if(m+50000) { 26 | Serial.print(">"); 27 | Serial.println((const char*)data); 28 | } 29 | }); 30 | m = millis(); 31 | } 32 | espNowFloodingMesh_loop(); 33 | delay(10); 34 | } 35 | -------------------------------------------------------------------------------- /examples/flooding mesh/slave/slave.ino: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #define ESP_NOW_CHANNEL 1 5 | //AES 128bit 6 | unsigned char secredKey[] = {0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}; 7 | 8 | void espNowFloodingMeshRecv(const uint8_t *data, int len, uint32_t replyPrt){ 9 | if (len > 0) { 10 | Serial.println((const char*)data); 11 | } 12 | } 13 | 14 | void setup() { 15 | Serial.begin(115200); 16 | //Set device in AP mode to begin with 17 | espNowFloodingMesh_RecvCB(espNowFloodingMeshRecv); 18 | espNowFloodingMesh_begin(ESP_NOW_CHANNEL); 19 | espNowFloodingMesh_secredkey(secredKey); 20 | espNowFloodingMesh_setToBatteryNode(); 21 | } 22 | 23 | void loop() { 24 | static unsigned long m = millis(); 25 | 26 | //Ask instant sync from master. 27 | espNowFloodingMesh_requestInstantTimeSyncFromMaster(); 28 | while (espNowFloodingMesh_isSyncedWithMaster() == false); 29 | char message[] = "SLAVE(12) HELLO MESSAGE"; 30 | espNowFloodingMesh_send((uint8_t*)message, sizeof(message), 3); //set ttl to 3 31 | espNowFloodingMesh_loop(); 32 | ESP.deepSleep(60000, WAKE_RF_DEFAULT); //Wakeup every minute 33 | } 34 | -------------------------------------------------------------------------------- /examples/start-network/master/master.ino: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | 4 | #define ESP_NOW_CHANNEL 1 5 | //AES 128bit 6 | unsigned char secredKey[] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE, 0xFF}; 7 | 8 | void espNowFloodingMeshRecv(const uint8_t *data, int len, uint32_t replyPrt){ 9 | if(len>0) { 10 | Serial.println((const char*)data); 11 | } 12 | } 13 | 14 | void setup() { 15 | Serial.begin(115200); 16 | //Set device in AP mode to begin with 17 | espNowFloodingMesh_RecvCB(espNowFloodingMeshRecv); 18 | espNowFloodingMesh_secredkey(secredKey); 19 | espNowFloodingMesh_begin(ESP_NOW_CHANNEL); 20 | espNowFloodingMesh_setToMasterRole(); 21 | } 22 | 23 | void loop() { 24 | static unsigned long m = millis(); 25 | if(m+5000 3 | 4 | #define ESP_NOW_CHANNEL 1 5 | //AES 128bit 6 | unsigned char secredKey[] = {0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF}; 7 | 8 | void espNowFloodingMeshRecv(const uint8_t *data, int len, uint32_t replyPrt){ 9 | if(len>0) { 10 | Serial.println((const char*)data); 11 | } 12 | } 13 | 14 | void setup() { 15 | Serial.begin(115200); 16 | //Set device in AP mode to begin with 17 | espNowFloodingMesh_RecvCB(espNowFloodingMeshRecv); 18 | espNowFloodingMesh_begin(ESP_NOW_CHANNEL); 19 | espNowFloodingMesh_secredkey(secredKey); 20 | 21 | //Ask instant sync from master. 22 | espNowFloodingMesh_requestInstantTimeSyncFromMaster(); 23 | while(espNowFloodingMesh_isSyncedWithMaster()==false); 24 | } 25 | 26 | void loop() { 27 | static unsigned long m = millis(); 28 | if(m+5000 5 | sentence=ESPNOW flooding mesh library v2 6 | category=Communication 7 | url=https://github.com/leodesigner/espNowFloodingMeshLibrary2 8 | architectures=esp32,espressif32,esp8266 9 | includes=EspNowFloodingMesh.h 10 | -------------------------------------------------------------------------------- /safememcpy.cpp: -------------------------------------------------------------------------------- 1 | 2 | 3 | char *memcpyS(char *dest, int destsz, const char *src, int count) 4 | { 5 | int i = 0; 6 | for (i = 0; (i < count) && (i < destsz); i++) 7 | { 8 | dest[i] = src[i]; 9 | } 10 | return &dest[i]; 11 | } 12 | -------------------------------------------------------------------------------- /safememcpy.h: -------------------------------------------------------------------------------- 1 | #ifndef __SAFE_OPERATIONS__H_ 2 | #define __SAFE_OPERATIONS__H_ 3 | 4 | char *memcpyS(char *dest, int destsz, const char *src, int count); 5 | 6 | #endif 7 | -------------------------------------------------------------------------------- /wifi802_11.cpp: -------------------------------------------------------------------------------- 1 | #include "wifi802_11.h" 2 | #ifdef ESP32 3 | #include 4 | #include "esp_wifi.h" 5 | #include 6 | #include 7 | #else 8 | #include 9 | #include 10 | #endif 11 | #include 12 | 13 | const char *ssid = "MESH_NETWORK"; 14 | char wifi_password[20]; 15 | 16 | #define BEACON_SSID_OFFSET 38 17 | #define SRCADDR_OFFSET 10 18 | #define BSSID_OFFSET 16 19 | #define MY_MAC_OFFSET 10 20 | #define SEQNUM_OFFSET 22 21 | #define DATA_START_OFFSET 24 22 | 23 | uint8_t raw_HEADER[] = { 24 | //MAC HEADER 25 | 0x40, 0x0C, // 0-1: Frame Control //Version 0 && Data Frame && MESH 26 | 0x00, 0x00, // 2-3: Duration 27 | 0xaa, 0xbb, 0xcc, 0xee, 0xff, 0x11, // 4-9: Destination address (broadcast) 28 | 0xba, 0xde, 0xaf, 0xfe, 0x00, 0x06, // 10-15: Source address 29 | 0xba, 0xde, 0xaf, 0xfe, 0x00, 0x06, // 16-21: BSSID 30 | 0x00, 0x00 // 22-23: Sequence / fragment number 31 | }; 32 | short sequence = 0; 33 | void (*wifi_802_receive_callback)(const uint8_t *, int, uint8_t) = NULL; 34 | 35 | #ifdef ESP32 36 | void receive_raw_cb(void *recv_buf, wifi_promiscuous_pkt_type_t type) 37 | { 38 | /* 39 | 20:35:41.710 -> BF 20 24 80 00 00 00 00 A1 00 01 B6 AC 32 16 00 __$__________2__ 40 | 20:35:41.710 -> 93 0A 06 22 00 00 60 63 24 40 02 00 40 0C 00 00 ___"__`c$@__@___ 41 | 20:35:41.710 -> FF FF FF FF FF FF BA DE AF FE 00 06 BA DE AF FE ________________ 42 | 20:35:41.710 -> 00 06 81 03 00 06 48 45 4C 4C 4F 32 D0 8D 3D 6A ______HELLO2__=j 43 | 20:35:41.710 -> 78 56 AD BA E5 A8 FB 3F 9C BC FB 3F AD BA AD BA xV_____?___?____ 44 | 20:35:41.743 -> E5 A8 FB 3F 9C BC FB 3F 00 00 00 00 00 00 00 00 ___?___?________ 45 | 20:35:41.743 -> 00 00 00 00 ____ 46 | 20:35:41.743 -> Length: 100 47 | 48 | */ 49 | wifi_promiscuous_pkt_t *sniffer = (wifi_promiscuous_pkt_t *)recv_buf; 50 | if (sniffer->payload[0] != 0x40) 51 | return; 52 | if (memcmp(sniffer->payload + BSSID_OFFSET, raw_HEADER + BSSID_OFFSET, 6) != 0) 53 | return; 54 | 55 | unsigned char *d = sniffer->payload + DATA_START_OFFSET; 56 | short length = ((unsigned short)d[0]) << 8 | d[1]; 57 | 58 | wifi_802_receive_callback(d + 2, length, sniffer->rx_ctrl.rssi); 59 | 60 | return; 61 | } 62 | #else 63 | void receive_raw_cb(unsigned char *frm, short unsigned int len) 64 | { 65 | /* 66 | 16:34:05.795 -> C3 10 26 50 00 00 00 00 00 00 01 00 [40 0C 00 00 __&P________@___ 67 | 16:34:05.795 -> FF FF FF FF FF FF BA DE AF FE 00 [06] BA DE AF FE ________________ 68 | 16:34:05.795 -> 00 06 00 00 48 65 6C 6C 6F 20 31 35 32 37 00 00 ____Hello_1527__ 69 | 16:34:05.828 -> 00 00 01 08 8B 96 82 84 0C 18 30 60 03 01 01 05 __________0`____ 70 | 16:34:05.828 -> 05 01 02 00 00 00 07 06 43 4E 00 01 0D 14 2A 01 ________CN____*_ 71 | 16:34:05.828 -> 00 32 04 6C 12 24 48 30 18 01 00 00 0F AC 02 02 _2_l_$H0________ 72 | 16:34:05.828 -> 00 00 0F AC ____ 73 | 16:34:05.828 -> Length: 100 74 | */ 75 | uint8_t rssi = frm[0]; 76 | //if(frm[0]!=0x40) return; 77 | 78 | if (frm[12] != 0x40) 79 | return; 80 | if (memcmp(frm + BSSID_OFFSET + 12, raw_HEADER + BSSID_OFFSET, 6) != 0) 81 | return; 82 | unsigned char *d = frm + 12 + DATA_START_OFFSET; 83 | 84 | short length = ((unsigned short)d[0]) << 8 | d[1]; 85 | 86 | if (wifi_802_receive_callback != NULL) 87 | { 88 | wifi_802_receive_callback(d + 2, length, rssi); 89 | } 90 | } 91 | #endif 92 | char password[15]; 93 | void wifi_802_11_begin(char bsId[], int channel) 94 | { 95 | //WiFi.begin(); 96 | String mac = WiFi.macAddress(); 97 | memcpy(raw_HEADER + BSSID_OFFSET, bsId, 6); 98 | memcpy(raw_HEADER + MY_MAC_OFFSET, mac.c_str(), 6); 99 | 100 | #ifdef ESP32 101 | esp_wifi_set_mode(WIFI_MODE_STA); 102 | esp_wifi_set_channel(channel, WIFI_SECOND_CHAN_NONE); 103 | esp_wifi_set_promiscuous_rx_cb(receive_raw_cb); 104 | esp_wifi_set_promiscuous(1); 105 | esp_wifi_set_max_tx_power(127); 106 | #else 107 | wifi_set_opmode(STATION_MODE); 108 | wifi_set_channel(channel); 109 | wifi_set_promiscuous_rx_cb(receive_raw_cb); 110 | wifi_promiscuous_enable(true); 111 | WiFi.setOutputPower(20.5); 112 | #endif 113 | } 114 | 115 | void wifi_802_receive_cb(void (*cb)(const uint8_t *, int, uint8_t)) 116 | { 117 | wifi_802_receive_callback = cb; 118 | } 119 | 120 | void wifi_802_11_send(const uint8_t *d, uint16_t len) 121 | { 122 | uint8_t buf[500]; 123 | for (int i = 0; i < 5; i++) 124 | { // really 5 times ? 125 | if (len > sizeof(buf) - sizeof(raw_HEADER) - 2) 126 | return; 127 | 128 | memcpy(buf, raw_HEADER, sizeof(raw_HEADER)); 129 | memcpy(buf + sizeof(raw_HEADER) + 2, d, len); 130 | memcpy(buf + SEQNUM_OFFSET, (char *)&sequence, 2); 131 | 132 | buf[sizeof(raw_HEADER)] = (len >> 8) & 0xff; 133 | buf[sizeof(raw_HEADER) + 1] = len & 0xff; 134 | 135 | #ifdef ESP32 136 | esp_wifi_80211_tx(ESP_IF_WIFI_STA, buf, sizeof(raw_HEADER) + len + 2, true); 137 | #else 138 | wifi_send_pkt_freedom(buf, sizeof(raw_HEADER) + len + 2, true); 139 | #endif 140 | sequence++; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /wifi802_11.h: -------------------------------------------------------------------------------- 1 | #ifndef WIFI_802_11_h__ 2 | #define WIFI_802_11_h__ 3 | #include 4 | 5 | void wifi_802_11_begin(char bsId[], int channel); 6 | void wifi_802_11_send(const uint8_t *d, uint16_t len); 7 | void wifi_802_receive_cb(void(*cb)(const uint8_t *, int, uint8_t)); 8 | 9 | #endif 10 | --------------------------------------------------------------------------------