From c96b29a5442ac1c1d9ea6c96dc308a88fdaecada Mon Sep 17 00:00:00 2001 From: Timm Bogner <64260873+timmbogner@users.noreply.github.com> Date: Fri, 12 Aug 2022 20:56:29 -0500 Subject: [PATCH 1/8] added new abilities --- .../ESPNOW_Controller/ESPNOW_Controller.ino | 41 ++ .../ESPNOW_Controller/fdrs_sensor_config.h | 39 ++ examples/ESPNOW_Sensor/ESPNOW_Sensor.ino | 2 +- examples/ESPNOW_Sensor/fdrs_sensor_config.h | 6 + src/fdrs_espnow.h | 20 +- src/fdrs_functions.h | 106 +++- src/fdrs_node.h | 505 ++++++++++++++++++ 7 files changed, 703 insertions(+), 16 deletions(-) create mode 100644 examples/ESPNOW_Controller/ESPNOW_Controller.ino create mode 100644 examples/ESPNOW_Controller/fdrs_sensor_config.h create mode 100644 src/fdrs_node.h diff --git a/examples/ESPNOW_Controller/ESPNOW_Controller.ino b/examples/ESPNOW_Controller/ESPNOW_Controller.ino new file mode 100644 index 0000000..0530ecd --- /dev/null +++ b/examples/ESPNOW_Controller/ESPNOW_Controller.ino @@ -0,0 +1,41 @@ +// FARM DATA RELAY SYSTEM +// +// ESP-NOW Sensor Example +// +// Developed by Timm Bogner (timmbogner@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. +// An example of how to send data via ESP-NOW using FDRS. +// + +#include "fdrs_sensor_config.h" +#include + +float data1; +float data2; + + + +void ctrl_1_cb(DataReading* theData){DBG("C1");} +void ctrl_2_cb(DataReading* theData){DBG("C2");} +void ctrl_3_cb(DataReading* theData){DBG("C3");} +void ctrl_4_cb(DataReading* theData){DBG("C4");} + +void setup() { + beginFDRS(); + addFDRS(1000); +} +void loop() { +// data1 = readHum(); +// loadFDRS(data1, HUMIDITY_T); +// data2 = readTemp(); +// loadFDRS(data2, TEMP_T); +// sendFDRS(); +// sleepFDRS(10); //Sleep time in seconds +} + +float readTemp() { + return 22.069; +} + +float readHum() { + return random(0,100); +} diff --git a/examples/ESPNOW_Controller/fdrs_sensor_config.h b/examples/ESPNOW_Controller/fdrs_sensor_config.h new file mode 100644 index 0000000..fd4400e --- /dev/null +++ b/examples/ESPNOW_Controller/fdrs_sensor_config.h @@ -0,0 +1,39 @@ +// FARM DATA RELAY SYSTEM +// +// Sensor Configuration + +#include + +#define READING_ID 1 //Unique ID for this sensor +#define GTWY_MAC 0x01 //Address of the nearest gateway + +#define CONTROL_1 133 //Address for controller 1 +#define CONTROL_2 134 //Address for controller 2 +#define CONTROL_3 135 //Address for controller 3 +#define CONTROL_4 136 //Address for controller 4 + + +#define USE_ESPNOW +//#define USE_LORA +#define DEEP_SLEEP +//#define POWER_CTRL 14 +#define FDRS_DEBUG + +//SPI Configuration -- Needed only on chipsets with multiple SPI interfaces (ESP32) +#define SPI_SCK 5 +#define SPI_MISO 19 +#define SPI_MOSI 27 + +//LoRa Configuration +#define LORA_SS 18 +#define LORA_RST 14 +#define LORA_DIO0 26 +//433E6 for Asia +//866E6 for Europe +//915E6 for North America +//#define LORA_BAND 915E6 // LoRa Frequency Band +//#define LORA_SF 7 // LoRa Spreading Factor +//#define LORA_TXPWR 17 // LoRa TX power in dBm (+2dBm - +20dBm), default is +17dBm +//#define LORA_ACK // Request LoRa acknowledgment. Increases battery usage. +//#define LORA_ACK_TIMEOUT 400 // LoRa ACK timeout in ms. (Minimum = 200) +//#define LORA_RETRIES 2 // LoRa ACK retries [0 - 3] diff --git a/examples/ESPNOW_Sensor/ESPNOW_Sensor.ino b/examples/ESPNOW_Sensor/ESPNOW_Sensor.ino index 6febf26..1cd48be 100644 --- a/examples/ESPNOW_Sensor/ESPNOW_Sensor.ino +++ b/examples/ESPNOW_Sensor/ESPNOW_Sensor.ino @@ -7,7 +7,7 @@ // #include "fdrs_sensor_config.h" -#include +#include float data1; float data2; diff --git a/examples/ESPNOW_Sensor/fdrs_sensor_config.h b/examples/ESPNOW_Sensor/fdrs_sensor_config.h index afe3326..fd4400e 100644 --- a/examples/ESPNOW_Sensor/fdrs_sensor_config.h +++ b/examples/ESPNOW_Sensor/fdrs_sensor_config.h @@ -7,6 +7,12 @@ #define READING_ID 1 //Unique ID for this sensor #define GTWY_MAC 0x01 //Address of the nearest gateway +#define CONTROL_1 133 //Address for controller 1 +#define CONTROL_2 134 //Address for controller 2 +#define CONTROL_3 135 //Address for controller 3 +#define CONTROL_4 136 //Address for controller 4 + + #define USE_ESPNOW //#define USE_LORA #define DEEP_SLEEP diff --git a/src/fdrs_espnow.h b/src/fdrs_espnow.h index debf03a..6a4fd69 100644 --- a/src/fdrs_espnow.h +++ b/src/fdrs_espnow.h @@ -1,4 +1,3 @@ - #ifdef USE_ESPNOW // Set ESP-NOW send and receive callbacks for either ESP8266 or ESP32 #if defined(ESP8266) @@ -10,6 +9,7 @@ void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { } void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { #endif + memcpy(&incMAC, mac, sizeof(incMAC)); if (len < sizeof(DataReading)) { DBG("ESP-NOW System Packet"); memcpy(&theCmd, incomingData, sizeof(theCmd)); @@ -17,7 +17,6 @@ void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { return; } memcpy(&theData, incomingData, sizeof(theData)); - memcpy(&incMAC, mac, sizeof(incMAC)); DBG("Incoming ESP-NOW."); ln = len / sizeof(DataReading); if (memcmp(&incMAC, &ESPNOW1, 6) == 0) { @@ -91,6 +90,23 @@ void begin_espnow() { #endif //USE_ESPNOW } +void sendESPNOWpeers() { +#ifdef USE_ESPNOW + DBG("Sending to ESP-NOW peers."); + DataReading thePacket[ln]; + int j = 0; + for (int i = 0; i < ln; i++) { + if ( j > espnow_size) { + j = 0; + esp_now_send(0, (uint8_t *) &thePacket, sizeof(thePacket)); + } + thePacket[j] = theData[i]; + j++; + } + esp_now_send(0, (uint8_t *) &thePacket, j * sizeof(DataReading)); +#endif // USE_ESPNOW +} + void sendESPNOW(uint8_t address) { #ifdef USE_ESPNOW diff --git a/src/fdrs_functions.h b/src/fdrs_functions.h index 3331d01..4e3031e 100644 --- a/src/fdrs_functions.h +++ b/src/fdrs_functions.h @@ -146,7 +146,11 @@ enum { #ifdef DEBUG_NODE_CONFIG #include "fdrs_checkConfig.h" #endif +typedef struct FDRSPeer { + uint8_t mac[6]; + uint32_t last_seen; +} FDRSPeer; typedef struct __attribute__((packed)) DataReading { float d; uint16_t id; @@ -159,6 +163,7 @@ typedef struct __attribute__((packed)) SystemPacket { uint32_t param; } SystemPacket; +FDRSPeer peer_list[16]; const uint8_t espnow_size = 250 / sizeof(DataReading); const uint8_t lora_size = 256 / sizeof(DataReading); const uint8_t mac_prefix[] = {MAC_PREFIX}; @@ -608,35 +613,110 @@ void begin_FS() { #endif // USE_FS_LOG } +int getFDRSPeer(uint8_t *mac) { // Returns the index of the array element that contains the provided MAC address + DBG("Getting peer #"); + + for (int i = 0; i < 16; i++) { + if (memcmp(mac, &peer_list[i].mac, 6) == 0) { + DBG("Peer is entry #" + String(i)); + return i; + } + } + + //DBG("Couldn't find peer"); + return -1; +} +int findOpenPeer() { // Returns an unused entry in peer_list, -1 if full. + uint8_t zero_addr[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; + for (int i = 0; i < 16; i++) { + if (memcmp(&zero_addr, &peer_list[i].mac, 6) == 0) { + DBG("Using peer entry " + String(i)); + return i; + } + } + DBG("No open peers"); + return -1; +} +int checkPeerExpired() { // Checks whether any entries in the peer_list have expired + for (int i = 0; i < 16; i++) { + if ((millis() - peer_list[i].last_seen) >= PEER_TIMEOUT) { + esp_now_del_peer(incMAC); + } + return -1; + } +} + + void handleCommands() { switch (theCmd.cmd) { case cmd_ping: DBG("Ping back to sender"); SystemPacket sys_packet; sys_packet.cmd = cmd_ping; + if (!esp_now_is_peer_exist(incMAC)) { +#ifdef ESP8266 + esp_now_add_peer(incMAC, ESP_NOW_ROLE_COMBO, 0, NULL, 0); +#endif #if defined(ESP32) - esp_now_peer_info_t peerInfo; - peerInfo.ifidx = WIFI_IF_STA; - peerInfo.channel = 0; - peerInfo.encrypt = false; - memcpy(peerInfo.peer_addr, incMAC, 6); - if (esp_now_add_peer(&peerInfo) != ESP_OK) { - DBG("Failed to add peer"); - return; - } + esp_now_peer_info_t peerInfo; + peerInfo.ifidx = WIFI_IF_STA; + peerInfo.channel = 0; + peerInfo.encrypt = false; + memcpy(peerInfo.peer_addr, incMAC, 6); + if (esp_now_add_peer(&peerInfo) != ESP_OK) { + DBG("Failed to add peer"); + return; + } #endif - esp_now_send(incMAC, (uint8_t *) &sys_packet, sizeof(SystemPacket)); + esp_now_send(incMAC, (uint8_t *) &sys_packet, sizeof(SystemPacket)); esp_now_del_peer(incMAC); + } else { + esp_now_send(incMAC, (uint8_t *) &sys_packet, sizeof(SystemPacket)); + } break; + case cmd_add: - DBG("Add sender to peer list (not completed)"); + DBG("Device requesting peer registration"); + int peer_num = getFDRSPeer(&incMAC[0]); + if (peer_num == -1) { //if the device isn't registered + DBG("Device not yet registered, adding to internal peer list"); + int open_peer = findOpenPeer(); // find open spot in peer_list + memcpy(&peer_list[open_peer].mac, &incMAC, 6); //save MAC to open spot + peer_list[open_peer].last_seen = millis(); +#if defined(ESP32) + esp_now_peer_info_t peerInfo; + peerInfo.ifidx = WIFI_IF_STA; + peerInfo.channel = 0; + peerInfo.encrypt = false; + memcpy(peerInfo.peer_addr, incMAC, 6); + if (esp_now_add_peer(&peerInfo) != ESP_OK) { + DBG("Failed to add peer"); + return; + } +#endif +#if defined(ESP8266) + esp_now_add_peer(incMAC, ESP_NOW_ROLE_COMBO, 0, NULL, 0); + +#endif + SystemPacket sys_packet = { .cmd = cmd_add, .param = PEER_TIMEOUT }; + esp_now_send(incMAC, (uint8_t *) &sys_packet, sizeof(SystemPacket)); + + } else { + DBG("Refreshing existing peer registration"); + peer_list[peer_num].last_seen = millis(); + SystemPacket sys_packet = { .cmd = cmd_add, .param = PEER_TIMEOUT }; + esp_now_send(incMAC, (uint8_t *) &sys_packet, sizeof(SystemPacket)); + + + } break; + } - is_ping = false; + theCmd.cmd = cmd_clear; theCmd.param = 0; -} +} #endif //__FDRS_FUNCTIONS_H__ diff --git a/src/fdrs_node.h b/src/fdrs_node.h new file mode 100644 index 0000000..246efec --- /dev/null +++ b/src/fdrs_node.h @@ -0,0 +1,505 @@ +// FARM DATA RELAY SYSTEM +// +// "fdrs_sensor.h" +// +// Developed by Timm Bogner (timmbogner@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. +// +#include +#if defined(ESP8266) +#include +#include +#elif defined(ESP32) +#include +#include +#include +#endif +#ifdef USE_LORA +#include +#endif + +// enable to get detailed info from where single configuration macros have been taken +#define DEBUG_NODE_CONFIG + +#ifdef USE_LORA + +// select LoRa band configuration +#if defined(LORA_BAND) +#define FDRS_BAND LORA_BAND +#elif defined (GLOBAL_LORA_BAND) +#define FDRS_BAND GLOBAL_LORA_BAND +#else +// ASSERT("NO LORA-BAND defined! Please define in fdrs_globals.h (recommended) or in fdrs_sensor_config.h"); +#endif //LORA_BAND + +// select LoRa SF configuration +#if defined(LORA_SF) +#define FDRS_SF LORA_SF +#elif defined (GLOBAL_LORA_SF) +#define FDRS_SF GLOBAL_LORA_SF +#else +// ASSERT("NO LORA-SF defined! Please define in fdrs_globals.h (recommended) or in fdrs_sensor_config.h"); +#endif //LORA_SF + +#endif //USE_LORA + +#ifdef FDRS_DEBUG +#define DBG(a) (Serial.println(a)) +#else +#define DBG(a) +#endif + +#define MAC_PREFIX 0xAA, 0xBB, 0xCC, 0xDD, 0xEE // Should only be changed if implementing multiple FDRS systems. + +#ifdef DEBUG_NODE_CONFIG +//#include "fdrs_checkConfig.h" +#endif + +typedef struct __attribute__((packed)) DataReading { + float d; + uint16_t id; + uint8_t t; + +} DataReading; + + +enum crcResult { + CRC_NULL, + CRC_OK, + CRC_BAD, +} returnCRC; + +enum { + cmd_clear, + cmd_ping, + cmd_add, + cmd_ack, +}; + +typedef struct __attribute__((packed)) SystemPacket { + uint8_t cmd; + uint32_t param; +} SystemPacket; + +const uint16_t espnow_size = 250 / sizeof(DataReading); +const uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; + + +uint8_t gatewayAddress[] = {MAC_PREFIX, GTWY_MAC}; +uint16_t gtwyAddress = ((gatewayAddress[4] << 8) | GTWY_MAC); +uint16_t LoRaAddress = 0x4200; +unsigned long transmitLoRaMsg = 0; // Number of total LoRa packets destined for us and of valid size +unsigned long msgOkLoRa = 0; // Number of total LoRa packets with valid CRC +uint32_t gtwy_timeout = 0; + +uint8_t incMAC[6]; +uint32_t wait_time = 0; +DataReading fdrsData[espnow_size]; +DataReading incData[espnow_size]; + +uint8_t data_count = 0; +bool is_ping = false; +bool is_added = false; +uint32_t last_refresh; +void ctrl_1_cb(DataReading* reading); +void ctrl_2_cb(DataReading* reading); +void ctrl_3_cb(DataReading* reading); +void ctrl_4_cb(DataReading* reading); + +// Set ESP-NOW send and receive callbacks for either ESP8266 or ESP32 +#if defined(ESP8266) +void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { +} +void OnDataRecv(uint8_t* mac, uint8_t *incomingData, uint8_t len) { +#elif defined(ESP32) +void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { +} +void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { +#endif + + memcpy(&incMAC, mac, sizeof(incMAC)); + + if (len < sizeof(DataReading)) { + SystemPacket command; + memcpy(&command, incomingData, sizeof(command)); + switch (command.cmd) { + case cmd_ping: + is_ping = true; + break; + case cmd_add: + + is_added = true; + gtwy_timeout = command.param; + + break; + } + } else{ + memcpy(&incData, incomingData, len); + int pkt_readings = len / sizeof(DataReading); + for (int i; i <= pkt_readings; i++) { //Cycle through array of incoming DataReadings for any addressed to this device + switch (incData[i].id) { + case CONTROL_1: + ctrl_1_cb(&incData[i]); + break; + case CONTROL_2: + ctrl_2_cb(&incData[i]); + break; + case CONTROL_3: + ctrl_3_cb(&incData[i]); + break; + case CONTROL_4: + ctrl_4_cb(&incData[i]); + break; + } + } + } +} + + +void beginFDRS() { +#ifdef FDRS_DEBUG + Serial.begin(115200); + // find out the reset reason + esp_reset_reason_t resetReason; + resetReason = esp_reset_reason(); +#endif + DBG("FDRS Sensor ID " + String(READING_ID) + " initializing..."); + DBG(" Gateway: " + String (GTWY_MAC, HEX)); +#ifdef POWER_CTRL + DBG("Powering up the sensor array!"); + pinMode(POWER_CTRL, OUTPUT); + digitalWrite(POWER_CTRL, 1); +#endif + // Init ESP-NOW for either ESP8266 or ESP32 and set MAC address +#ifdef USE_ESPNOW + DBG("Initializing ESP-NOW!"); + WiFi.mode(WIFI_STA); + WiFi.disconnect(); +#if defined(ESP8266) + if (esp_now_init() != 0) { + return; + } + esp_now_set_self_role(ESP_NOW_ROLE_COMBO); + esp_now_register_recv_cb(OnDataRecv); + + // Register peers + esp_now_add_peer(gatewayAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); +#elif defined(ESP32) + + if (esp_now_init() != ESP_OK) { + DBG("Error initializing ESP-NOW"); + return; + } + esp_now_register_recv_cb(OnDataRecv); + + esp_now_peer_info_t peerInfo; + peerInfo.ifidx = WIFI_IF_STA; + peerInfo.channel = 0; + peerInfo.encrypt = false; + // Register first peer + memcpy(peerInfo.peer_addr, broadcast_mac, 6); + if (esp_now_add_peer(&peerInfo) != ESP_OK) { + DBG("Failed to add peer bcast"); + return; + } + memcpy(peerInfo.peer_addr, gatewayAddress, 6); + if (esp_now_add_peer(&peerInfo) != ESP_OK) { + DBG("Failed to add peer"); + return; + } +#endif + DBG(" ESP-NOW Initialized."); +#endif //USE_ESPNOW +#ifdef USE_LORA + DBG("Initializing LoRa!"); +#ifdef ESP32 + SPI.begin(SPI_SCK, SPI_MISO, SPI_MOSI); +#endif + LoRa.setPins(LORA_SS, LORA_RST, LORA_DIO0); + if (!LoRa.begin(FDRS_BAND)) { + DBG("Unable to initialize LoRa!"); + while (1); + } + LoRa.setSpreadingFactor(FDRS_SF); + DBG(" LoRa Initialized."); + + DBG("LoRa Band: " + String(FDRS_BAND)); + DBG("LoRa SF : " + String(FDRS_SF)); +#endif // USE_LORA +#ifdef DEBUG_NODE_CONFIG + if (resetReason != ESP_RST_DEEPSLEEP) { + //checkConfig(); + } +#endif //DEBUG_NODE_CONFIG + +} + +// USED to get ACKs from LoRa gateway at this point. May be used in the future to get other data +// Return type is crcResult struct - CRC_OK, CRC_BAD, CRC_NULL. CRC_NULL used for non-ack data +crcResult getLoRa() { +#ifdef USE_LORA + int packetSize = LoRa.parsePacket(); + if ((packetSize - 6) % sizeof(SystemPacket) == 0 && packetSize > 0) { // packet size should be 6 bytes plus multiple of size of SystemPacket + uint8_t packet[packetSize]; + uint16_t sourceMAC = 0x0000; + uint16_t destMAC = 0x0000; + uint16_t packetCRC = 0x0000; // CRC Extracted from received LoRa packet + uint16_t calcCRC = 0x0000; // CRC calculated from received LoRa packet + uint ln = (packetSize - 6) / sizeof(SystemPacket); + SystemPacket receiveData[ln]; + + LoRa.readBytes((uint8_t *)&packet, packetSize); + + destMAC = (packet[0] << 8) | packet[1]; + sourceMAC = (packet[2] << 8) | packet[3]; + packetCRC = ((packet[packetSize - 2] << 8) | packet[packetSize - 1]); + DBG("Incoming LoRa. Size: " + String(packetSize) + " Bytes, RSSI: " + String(LoRa.packetRssi()) + "dBi, SNR: " + String(LoRa.packetSnr()) + "dB, PacketCRC: 0x" + String(packetCRC, 16)); + if (destMAC == LoRaAddress) { + //printLoraPacket(packet,sizeof(packet)); + memcpy(receiveData, &packet[4], packetSize - 6); //Split off data portion of packet (N bytes) + if (ln == 1 && receiveData[0].cmd == cmd_ack) { // We have received an ACK packet + if (packetCRC == 0xFFFF) { + DBG("ACK Received - address 0x" + String(sourceMAC, 16) + "(hex) does not want ACKs"); + return CRC_OK; + } + else { + for (int i = 0; i < (packetSize - 2); i++) { // Last 2 bytes of packet are the CRC so do not include them in calculation + //printf("CRC: %02X : %d\n",calcCRC, i); + calcCRC = crc16_update(calcCRC, packet[i]); + } + if (calcCRC == packetCRC) { + DBG("ACK Received - CRC Match"); + return CRC_OK; + } + else { + DBG("ACK Received CRC Mismatch! Packet CRC is 0x" + String(packetCRC, 16) + ", Calculated CRC is 0x" + String(calcCRC, 16)); + return CRC_BAD; + } + } + } + else { // data we have received is not of type ACK_T. How we handle is future enhancement. + DBG("Received some LoRa SystemPacket data that is not of type ACK. To be handled in future enhancement."); + DBG("ln: " + String(ln) + "data type: " + String(receiveData[0].cmd)); + return CRC_NULL; + } + } + else if ((packetSize - 6) % sizeof(DataReading) == 0) { // packet size should be 6 bytes plus multiple of size of DataReading) + DBG("Incoming LoRa packet of " + String(packetSize) + " bytes received, with DataReading data to be processed."); + return CRC_NULL; + } + else { + DBG("Incoming LoRa packet of " + String(packetSize) + " bytes received, not destined to our address."); + return CRC_NULL; + } + } + else { + if (packetSize != 0) { + DBG("Incoming LoRa packet of " + String(packetSize) + " bytes received"); + } + } + return CRC_NULL; +#endif +} + +void printLoraPacket(uint8_t* p, int size) { + printf("Printing packet of size %d.", size); + for (int i = 0; i < size; i++ ) { + if (i % 2 == 0) printf("\n%02d: ", i); + printf("%02X ", p[i]); + } + printf("\n"); +} + +void transmitLoRa(uint16_t* destMAC, DataReading * packet, uint8_t len) { +#ifdef USE_LORA + uint8_t pkt[6 + (len * sizeof(DataReading))]; + uint16_t calcCRC = 0x0000; + + pkt[0] = (*destMAC >> 8); + pkt[1] = (*destMAC & 0x00FF); + pkt[2] = (LoRaAddress >> 8); + pkt[3] = (LoRaAddress & 0x00FF); + memcpy(&pkt[4], packet, len * sizeof(DataReading)); + for (int i = 0; i < (sizeof(pkt) - 2); i++) { // Last 2 bytes are CRC so do not include them in the calculation itself + //printf("CRC: %02X : %d\n",calcCRC, i); + calcCRC = crc16_update(calcCRC, pkt[i]); + } +#ifndef LORA_ACK + calcCRC = crc16_update(calcCRC, 0xA1); // Recalculate CRC for No ACK +#endif // LORA_ACK + pkt[len * sizeof(DataReading) + 4] = (calcCRC >> 8); + pkt[len * sizeof(DataReading) + 5] = (calcCRC & 0x00FF); +#ifdef LORA_ACK // Wait for ACK + int retries = LORA_RETRIES + 1; + while (retries != 0) { + if (transmitLoRaMsg != 0) + DBG("Transmitting LoRa message of size " + String(sizeof(pkt)) + " bytes with CRC 0x" + String(calcCRC, 16) + " to gateway 0x" + String(*destMAC, 16) + ". Retries remaining: " + String(retries - 1) + ", CRC OK " + String((float)msgOkLoRa / transmitLoRaMsg * 100) + "%"); + else + DBG("Transmitting LoRa message of size " + String(sizeof(pkt)) + " bytes with CRC 0x" + String(calcCRC, 16) + " to gateway 0x" + String(*destMAC, 16) + ". Retries remaining: " + String(retries - 1)); + //printLoraPacket(pkt,sizeof(pkt)); + LoRa.beginPacket(); + LoRa.write((uint8_t*)&pkt, sizeof(pkt)); + LoRa.endPacket(); + transmitLoRaMsg++; + unsigned long loraAckTimeout = millis() + LORA_ACK_TIMEOUT; + retries--; + delay(10); + while (returnCRC == CRC_NULL && (millis() < loraAckTimeout)) { + returnCRC = getLoRa(); + } + if (returnCRC == CRC_OK) { + //DBG("LoRa ACK Received! CRC OK"); + msgOkLoRa++; + return; // we're done + } + else if (returnCRC == CRC_BAD) { + //DBG("LoRa ACK Received! CRC BAD"); + // Resend original packet again if retries are available + } + else { + DBG("LoRa Timeout waiting for ACK!"); + // resend original packet again if retries are available + } + } +#else // Send and do not wait for ACK reply + DBG("Transmitting LoRa message of size " + String(sizeof(pkt)) + " bytes with CRC 0x" + String(calcCRC, 16) + " to gateway 0x" + String(*destMAC, 16)); + //printLoraPacket(pkt,sizeof(pkt)); + LoRa.beginPacket(); + LoRa.write((uint8_t*)&pkt, sizeof(pkt)); + LoRa.endPacket(); + transmitLoRaMsg++; +#endif // LORA_ACK +#endif // USE_LORA +} + +void sendFDRS() { + DBG("Sending FDRS Packet!"); +#ifdef USE_ESPNOW + esp_now_send(gatewayAddress, (uint8_t *) &fdrsData, data_count * sizeof(DataReading)); + delay(5); + DBG(" ESP-NOW sent."); +#endif +#ifdef USE_LORA + transmitLoRa(>wyAddress, fdrsData, data_count); +#endif + data_count = 0; + returnCRC = CRC_NULL; +} + +void loadFDRS(float d, uint8_t t) { + DBG("Id: " + String(READING_ID) + " - Type: " + String(t) + " - Data loaded: " + String(d)); + if (data_count > espnow_size) sendFDRS(); + DataReading dr; + dr.id = READING_ID; + dr.t = t; + dr.d = d; + fdrsData[data_count] = dr; + data_count++; +} + +void sleepFDRS(int sleep_time) { + DBG("Sleepytime!"); +#ifdef DEEP_SLEEP + DBG(" Deep sleeping."); +#ifdef ESP32 + esp_sleep_enable_timer_wakeup(sleep_time * 1000000); + esp_deep_sleep_start(); +#endif +#ifdef ESP8266 + ESP.deepSleep(sleep_time * 1000000); +#endif +#endif + DBG(" Delaying."); + delay(sleep_time * 1000); +} + + +void loopFDRS() { + if (is_added) { + if ((millis() - last_refresh) >= gtwy_timeout) { + last_refresh = millis(); + } + } +} +uint32_t pingFDRS(int timeout) { + SystemPacket sys_packet = { .cmd = cmd_ping, .param = 0 }; +#ifdef USE_ESPNOW + esp_now_send(gatewayAddress, (uint8_t *) &sys_packet, sizeof(SystemPacket)); + DBG(" ESP-NOW ping sent."); + uint32_t ping_start = millis(); + is_ping = false; + while ((millis() - ping_start) <= timeout) { + yield(); //do I need to yield or does it automatically? + if (is_ping) { + DBG("Ping Returned:" + String(millis() - ping_start) + " from " + String(incMAC[5])); + return millis() - ping_start; + } + } +#endif +#ifdef USE_LORA + //transmitLoRa(gtwyAddress, sys_packet, data_count); // TODO: Make this congruent to esp_now_send() + DBG(" LoRa ping not sent because it isn't implemented."); +#endif +} +bool seekFDRS(int timeout) { + SystemPacket sys_packet = { .cmd = cmd_ping, .param = 0 }; +#ifdef USE_ESPNOW + esp_now_send(broadcast_mac, (uint8_t *) &sys_packet, sizeof(SystemPacket)); + DBG("Seeking nearby gateways"); + uint32_t ping_start = millis(); + is_ping = false; + while ((millis() - ping_start) <= timeout) { + yield(); //do I need to yield or does it automatically? + if (is_ping) { + DBG("Responded:" + String(incMAC[5])); + return true; + } + } + return false; + +#endif +} + +bool addFDRS(int timeout) { + SystemPacket sys_packet = { .cmd = cmd_add, .param = 0 }; + esp_now_send(gatewayAddress, (uint8_t *) &sys_packet, sizeof(SystemPacket)); + DBG("ESP-NOW peer subscription submitted to " + String(gatewayAddress[5])); + uint32_t add_start = millis(); + is_added = false; + while ((millis() - add_start) <= timeout) { + if (is_added) { + DBG("Subscription accepted. Timeout: " + String(gtwy_timeout)); + last_refresh = millis(); + return true; + } + } + DBG("No gateways accepted the request"); + return false; + } + + +// CRC16 from https://github.com/4-20ma/ModbusMaster/blob/3a05ff87677a9bdd8e027d6906dc05ca15ca8ade/src/util/crc16.h#L71 + +/** @ingroup util_crc16 + Processor-independent CRC-16 calculation. + Polynomial: x^16 + x^15 + x^2 + 1 (0xA001)
+ Initial value: 0xFFFF + This CRC is normally used in disk-drive controllers. + @param uint16_t crc (0x0000..0xFFFF) + @param uint8_t a (0x00..0xFF) + @return calculated CRC (0x0000..0xFFFF) +*/ + +static uint16_t crc16_update(uint16_t crc, uint8_t a) { + int i; + + crc ^= a; + for (i = 0; i < 8; ++i) + { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else + crc = (crc >> 1); + } + + return crc; +} From 500583db69f5e0a8107cd4214f9f8011b9a49242 Mon Sep 17 00:00:00 2001 From: Timm Bogner <64260873+timmbogner@users.noreply.github.com> Date: Sat, 13 Aug 2022 07:47:40 -0500 Subject: [PATCH 2/8] cosmetic --- .../ESPNOW_Controller/ESPNOW_Controller.ino | 30 ++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/examples/ESPNOW_Controller/ESPNOW_Controller.ino b/examples/ESPNOW_Controller/ESPNOW_Controller.ino index 0530ecd..824800f 100644 --- a/examples/ESPNOW_Controller/ESPNOW_Controller.ino +++ b/examples/ESPNOW_Controller/ESPNOW_Controller.ino @@ -14,22 +14,30 @@ float data2; -void ctrl_1_cb(DataReading* theData){DBG("C1");} -void ctrl_2_cb(DataReading* theData){DBG("C2");} -void ctrl_3_cb(DataReading* theData){DBG("C3");} -void ctrl_4_cb(DataReading* theData){DBG("C4");} +void ctrl_1_cb(DataReading* theData) { + DBG("Controller1"); +} +void ctrl_2_cb(DataReading* theData) { + DBG("Controller2"); +} +void ctrl_3_cb(DataReading* theData) { + DBG("Controller3"); +} +void ctrl_4_cb(DataReading* theData) { + DBG("Controller4"); +} void setup() { beginFDRS(); addFDRS(1000); } void loop() { -// data1 = readHum(); -// loadFDRS(data1, HUMIDITY_T); -// data2 = readTemp(); -// loadFDRS(data2, TEMP_T); -// sendFDRS(); -// sleepFDRS(10); //Sleep time in seconds + // data1 = readHum(); + // loadFDRS(data1, HUMIDITY_T); + // data2 = readTemp(); + // loadFDRS(data2, TEMP_T); + // sendFDRS(); + // sleepFDRS(10); //Sleep time in seconds } float readTemp() { @@ -37,5 +45,5 @@ float readTemp() { } float readHum() { - return random(0,100); + return random(0, 100); } From 989bff2474bf0fff9a283dbfe948859b29d03b55 Mon Sep 17 00:00:00 2001 From: Timm Bogner <64260873+timmbogner@users.noreply.github.com> Date: Sat, 13 Aug 2022 20:47:10 -0500 Subject: [PATCH 3/8] i guess that fixes it --- .../ESPNOW_Controller/ESPNOW_Controller.ino | 13 ++-- .../ESPNOW_Controller/fdrs_sensor_config.h | 2 +- src/fdrs_node.h | 63 ++++++++++--------- 3 files changed, 42 insertions(+), 36 deletions(-) diff --git a/examples/ESPNOW_Controller/ESPNOW_Controller.ino b/examples/ESPNOW_Controller/ESPNOW_Controller.ino index 824800f..a44d594 100644 --- a/examples/ESPNOW_Controller/ESPNOW_Controller.ino +++ b/examples/ESPNOW_Controller/ESPNOW_Controller.ino @@ -30,14 +30,15 @@ void ctrl_4_cb(DataReading* theData) { void setup() { beginFDRS(); addFDRS(1000); + pingFDRS(1000); } void loop() { - // data1 = readHum(); - // loadFDRS(data1, HUMIDITY_T); - // data2 = readTemp(); - // loadFDRS(data2, TEMP_T); - // sendFDRS(); - // sleepFDRS(10); //Sleep time in seconds +// data1 = readHum(); +// loadFDRS(data1, HUMIDITY_T); +// data2 = readTemp(); +// loadFDRS(data2, TEMP_T); +// sendFDRS(); +// sleepFDRS(10); //Sleep time in seconds } float readTemp() { diff --git a/examples/ESPNOW_Controller/fdrs_sensor_config.h b/examples/ESPNOW_Controller/fdrs_sensor_config.h index fd4400e..7e02433 100644 --- a/examples/ESPNOW_Controller/fdrs_sensor_config.h +++ b/examples/ESPNOW_Controller/fdrs_sensor_config.h @@ -15,7 +15,7 @@ #define USE_ESPNOW //#define USE_LORA -#define DEEP_SLEEP +//#define DEEP_SLEEP //#define POWER_CTRL 14 #define FDRS_DEBUG diff --git a/src/fdrs_node.h b/src/fdrs_node.h index 246efec..02ab5f9 100644 --- a/src/fdrs_node.h +++ b/src/fdrs_node.h @@ -81,7 +81,7 @@ typedef struct __attribute__((packed)) SystemPacket { } SystemPacket; const uint16_t espnow_size = 250 / sizeof(DataReading); -const uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; +uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; uint8_t gatewayAddress[] = {MAC_PREFIX, GTWY_MAC}; @@ -126,10 +126,8 @@ void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { is_ping = true; break; case cmd_add: - is_added = true; gtwy_timeout = command.param; - break; } } else{ @@ -158,9 +156,9 @@ void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { void beginFDRS() { #ifdef FDRS_DEBUG Serial.begin(115200); - // find out the reset reason - esp_reset_reason_t resetReason; - resetReason = esp_reset_reason(); + // // find out the reset reason + // esp_reset_reason_t resetReason; + // resetReason = esp_reset_reason(); #endif DBG("FDRS Sensor ID " + String(READING_ID) + " initializing..."); DBG(" Gateway: " + String (GTWY_MAC, HEX)); @@ -226,9 +224,9 @@ void beginFDRS() { DBG("LoRa SF : " + String(FDRS_SF)); #endif // USE_LORA #ifdef DEBUG_NODE_CONFIG - if (resetReason != ESP_RST_DEEPSLEEP) { + // if (resetReason != ESP_RST_DEEPSLEEP) { //checkConfig(); - } + // } #endif //DEBUG_NODE_CONFIG } @@ -420,26 +418,10 @@ void loopFDRS() { } } } -uint32_t pingFDRS(int timeout) { - SystemPacket sys_packet = { .cmd = cmd_ping, .param = 0 }; -#ifdef USE_ESPNOW - esp_now_send(gatewayAddress, (uint8_t *) &sys_packet, sizeof(SystemPacket)); - DBG(" ESP-NOW ping sent."); - uint32_t ping_start = millis(); - is_ping = false; - while ((millis() - ping_start) <= timeout) { - yield(); //do I need to yield or does it automatically? - if (is_ping) { - DBG("Ping Returned:" + String(millis() - ping_start) + " from " + String(incMAC[5])); - return millis() - ping_start; - } - } -#endif -#ifdef USE_LORA - //transmitLoRa(gtwyAddress, sys_packet, data_count); // TODO: Make this congruent to esp_now_send() - DBG(" LoRa ping not sent because it isn't implemented."); -#endif -} + + + + bool seekFDRS(int timeout) { SystemPacket sys_packet = { .cmd = cmd_ping, .param = 0 }; #ifdef USE_ESPNOW @@ -461,11 +443,13 @@ bool seekFDRS(int timeout) { bool addFDRS(int timeout) { SystemPacket sys_packet = { .cmd = cmd_add, .param = 0 }; + #ifdef USE_ESPNOW esp_now_send(gatewayAddress, (uint8_t *) &sys_packet, sizeof(SystemPacket)); DBG("ESP-NOW peer subscription submitted to " + String(gatewayAddress[5])); uint32_t add_start = millis(); is_added = false; - while ((millis() - add_start) <= timeout) { + while ((millis() - add_start) <= timeout) { + yield(); if (is_added) { DBG("Subscription accepted. Timeout: " + String(gtwy_timeout)); last_refresh = millis(); @@ -474,8 +458,29 @@ bool addFDRS(int timeout) { } DBG("No gateways accepted the request"); return false; + #endif } +uint32_t pingFDRS(int timeout) { + SystemPacket sys_packet = { .cmd = cmd_ping, .param = 0 }; +#ifdef USE_ESPNOW + esp_now_send(gatewayAddress, (uint8_t *) &sys_packet, sizeof(SystemPacket)); + DBG(" ESP-NOW ping sent."); + uint32_t ping_start = millis(); + is_ping = false; + while ((millis() - ping_start) <= timeout) { + yield(); //do I need to yield or does it automatically? + if (is_ping) { + DBG("Ping Returned:" + String(millis() - ping_start) + " from " + String(incMAC[5])); + return millis() - ping_start; + } + } +#endif +#ifdef USE_LORA + //transmitLoRa(gtwyAddress, sys_packet, data_count); // TODO: Make this congruent to esp_now_send() + DBG(" LoRa ping not sent because it isn't implemented."); +#endif +} // CRC16 from https://github.com/4-20ma/ModbusMaster/blob/3a05ff87677a9bdd8e027d6906dc05ca15ca8ade/src/util/crc16.h#L71 From c9a5142b9e8686ba70d4b61d9b68efe43dc5517b Mon Sep 17 00:00:00 2001 From: Timm Bogner <64260873+timmbogner@users.noreply.github.com> Date: Sun, 14 Aug 2022 00:07:34 -0500 Subject: [PATCH 4/8] fixed gateway --- examples/1_UART_Gateway/fdrs_gateway_config.h | 2 ++ src/fdrs_functions.h | 19 ++++++++++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/examples/1_UART_Gateway/fdrs_gateway_config.h b/examples/1_UART_Gateway/fdrs_gateway_config.h index 33c7976..1623718 100644 --- a/examples/1_UART_Gateway/fdrs_gateway_config.h +++ b/examples/1_UART_Gateway/fdrs_gateway_config.h @@ -76,6 +76,8 @@ #define LORA1_DELAY 500 //#define LORA2_DELAY 1000 +#define PEER_TIMEOUT 300000 + // FastLED -- Not yet fully implemented //#define USE_LED #define LED_PIN 32 diff --git a/src/fdrs_functions.h b/src/fdrs_functions.h index 4e3031e..dd61419 100644 --- a/src/fdrs_functions.h +++ b/src/fdrs_functions.h @@ -148,7 +148,7 @@ enum { #endif typedef struct FDRSPeer { uint8_t mac[6]; - uint32_t last_seen; + uint32_t last_seen = 0; } FDRSPeer; typedef struct __attribute__((packed)) DataReading { @@ -626,13 +626,21 @@ int getFDRSPeer(uint8_t *mac) { // Returns the index of the array element that //DBG("Couldn't find peer"); return -1; } -int findOpenPeer() { // Returns an unused entry in peer_list, -1 if full. - uint8_t zero_addr[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +int findOpenPeer() { // Returns an expired entry in peer_list, -1 if full. + //uint8_t zero_addr[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; for (int i = 0; i < 16; i++) { - if (memcmp(&zero_addr, &peer_list[i].mac, 6) == 0) { + if (peer_list[i].last_seen == 0){ DBG("Using peer entry " + String(i)); return i; - } + } + } + for (int i = 0; i < 16; i++) { + if ((millis() - peer_list[i].last_seen) >= PEER_TIMEOUT){ + DBG("Recycling peer entry " + String(i)); + esp_now_del_peer(peer_list[i].mac); + + return i; + } } DBG("No open peers"); return -1; @@ -704,6 +712,7 @@ void handleCommands() { } else { DBG("Refreshing existing peer registration"); peer_list[peer_num].last_seen = millis(); + SystemPacket sys_packet = { .cmd = cmd_add, .param = PEER_TIMEOUT }; esp_now_send(incMAC, (uint8_t *) &sys_packet, sizeof(SystemPacket)); From cba2b9562b65e1a45c1d607cb8f7cf83de98aa93 Mon Sep 17 00:00:00 2001 From: Timm Bogner <64260873+timmbogner@users.noreply.github.com> Date: Sun, 14 Aug 2022 00:16:59 -0500 Subject: [PATCH 5/8] Update 1_UART_Gateway.ino --- examples/1_UART_Gateway/1_UART_Gateway.ino | 2 ++ 1 file changed, 2 insertions(+) diff --git a/examples/1_UART_Gateway/1_UART_Gateway.ino b/examples/1_UART_Gateway/1_UART_Gateway.ino index 0f0f070..ccbb31f 100644 --- a/examples/1_UART_Gateway/1_UART_Gateway.ino +++ b/examples/1_UART_Gateway/1_UART_Gateway.ino @@ -48,12 +48,14 @@ void setup() { DBG("Address:" + String (UNIT_MAC, HEX)); #ifdef DEBUG_NODE_CONFIG +#ifdef ESP32 // find out the reset reason esp_reset_reason_t resetReason; resetReason = esp_reset_reason(); if (resetReason != ESP_RST_DEEPSLEEP) { checkConfig(); } +#endif //ESP32 #endif //DEBUG_NODE_CONFIG #ifdef USE_LED From 7dc8c64c73bded473c54a3b0da1a19afef42331d Mon Sep 17 00:00:00 2001 From: Timm Bogner <64260873+timmbogner@users.noreply.github.com> Date: Sun, 14 Aug 2022 00:35:59 -0500 Subject: [PATCH 6/8] Update fdrs_gateway_config.h --- examples/1_UART_Gateway/fdrs_gateway_config.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/1_UART_Gateway/fdrs_gateway_config.h b/examples/1_UART_Gateway/fdrs_gateway_config.h index 1623718..4ee35ff 100644 --- a/examples/1_UART_Gateway/fdrs_gateway_config.h +++ b/examples/1_UART_Gateway/fdrs_gateway_config.h @@ -11,7 +11,7 @@ // Current function options are: sendESPNOW(MAC), sendSerial(), sendMQTT(), // bufferLoRa(interface), bufferESPNOW(interface), bufferSerial(), and bufferMQTT(). -#define ESPNOWG_ACT sendSerial(); +#define ESPNOWG_ACT sendSerial(); sendESPNOWpeers(); #define SERIAL_ACT sendESPNOW(0x02); bufferLoRa(1); #define MQTT_ACT #define LORAG_ACT sendSerial(); @@ -20,7 +20,7 @@ // Warning: ESP-NOW and WiFi should not be used simultaneously. #define USE_ESPNOW -#define USE_LORA +//#define USE_LORA //#define USE_WIFI //Used only for MQTT gateway // Peer addresses From 378d50eb87f07cc38ca5c113198d1dc67d330356 Mon Sep 17 00:00:00 2001 From: Timm Bogner <64260873+timmbogner@users.noreply.github.com> Date: Mon, 22 Aug 2022 20:55:20 -0500 Subject: [PATCH 7/8] added physical addressing --- README.md | 2 +- src/fdrs_node.h | 62 ++++++++++++++++++++++++------------------------- 2 files changed, 32 insertions(+), 32 deletions(-) diff --git a/README.md b/README.md index d2eefed..d389d23 100644 --- a/README.md +++ b/README.md @@ -13,7 +13,7 @@ Install the libraries that you need: - [ArduinoJson](https://arduinojson.org/) (mandatory) - [LoRa library](https://github.com/sandeepmistry/arduino-LoRa) by sandeepmistry (required if using LoRa) - [PubSubClient](https://github.com/knolleary/pubsubclient/) (required for MQTT Gateways) - +- [ArduinoUniqueID](https://github.com/ricaun/ArduinoUniqueID) (required for LoRa sensors/controllers) ### [Sensors](/extras/Sensor.md) Sensors gather data and send it to a gateway via ESP-NOW or LoRa. diff --git a/src/fdrs_node.h b/src/fdrs_node.h index 02ab5f9..edd13bf 100644 --- a/src/fdrs_node.h +++ b/src/fdrs_node.h @@ -14,6 +14,7 @@ #include #endif #ifdef USE_LORA +#include #include #endif @@ -82,15 +83,14 @@ typedef struct __attribute__((packed)) SystemPacket { const uint16_t espnow_size = 250 / sizeof(DataReading); uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; - - uint8_t gatewayAddress[] = {MAC_PREFIX, GTWY_MAC}; uint16_t gtwyAddress = ((gatewayAddress[4] << 8) | GTWY_MAC); -uint16_t LoRaAddress = 0x4200; +#ifdef USE_LORA +uint16_t LoRaAddress = ((UniqueID8[6] << 8) | UniqueID8[7]); unsigned long transmitLoRaMsg = 0; // Number of total LoRa packets destined for us and of valid size unsigned long msgOkLoRa = 0; // Number of total LoRa packets with valid CRC +#endif uint32_t gtwy_timeout = 0; - uint8_t incMAC[6]; uint32_t wait_time = 0; DataReading fdrsData[espnow_size]; @@ -231,6 +231,33 @@ void beginFDRS() { } +// CRC16 from https://github.com/4-20ma/ModbusMaster/blob/3a05ff87677a9bdd8e027d6906dc05ca15ca8ade/src/util/crc16.h#L71 + +/** @ingroup util_crc16 + Processor-independent CRC-16 calculation. + Polynomial: x^16 + x^15 + x^2 + 1 (0xA001)
+ Initial value: 0xFFFF + This CRC is normally used in disk-drive controllers. + @param uint16_t crc (0x0000..0xFFFF) + @param uint8_t a (0x00..0xFF) + @return calculated CRC (0x0000..0xFFFF) +*/ + +static uint16_t crc16_update(uint16_t crc, uint8_t a) { + int i; + + crc ^= a; + for (i = 0; i < 8; ++i) + { + if (crc & 1) + crc = (crc >> 1) ^ 0xA001; + else + crc = (crc >> 1); + } + + return crc; +} + // USED to get ACKs from LoRa gateway at this point. May be used in the future to get other data // Return type is crcResult struct - CRC_OK, CRC_BAD, CRC_NULL. CRC_NULL used for non-ack data crcResult getLoRa() { @@ -481,30 +508,3 @@ uint32_t pingFDRS(int timeout) { DBG(" LoRa ping not sent because it isn't implemented."); #endif } - -// CRC16 from https://github.com/4-20ma/ModbusMaster/blob/3a05ff87677a9bdd8e027d6906dc05ca15ca8ade/src/util/crc16.h#L71 - -/** @ingroup util_crc16 - Processor-independent CRC-16 calculation. - Polynomial: x^16 + x^15 + x^2 + 1 (0xA001)
- Initial value: 0xFFFF - This CRC is normally used in disk-drive controllers. - @param uint16_t crc (0x0000..0xFFFF) - @param uint8_t a (0x00..0xFF) - @return calculated CRC (0x0000..0xFFFF) -*/ - -static uint16_t crc16_update(uint16_t crc, uint8_t a) { - int i; - - crc ^= a; - for (i = 0; i < 8; ++i) - { - if (crc & 1) - crc = (crc >> 1) ^ 0xA001; - else - crc = (crc >> 1); - } - - return crc; -} From eaf3af20cf91997bf8057bf12bd11b7074bc1a85 Mon Sep 17 00:00:00 2001 From: Timm Bogner <64260873+timmbogner@users.noreply.github.com> Date: Wed, 24 Aug 2022 23:33:02 -0500 Subject: [PATCH 8/8] subscription list and unified callback the functionality is almost complete --- examples/0_MQTT_Gateway/fdrs_gateway_config.h | 4 +- examples/1_UART_Gateway/1_UART_Gateway.ino | 2 +- examples/1_UART_Gateway/fdrs_gateway_config.h | 6 +- .../ESPNOW_Controller/ESPNOW_Controller.ino | 45 ++++-------- .../ESPNOW_Controller/fdrs_sensor_config.h | 6 -- src/fdrs_functions.h | 2 +- src/fdrs_node.h | 72 +++++++++++++------ 7 files changed, 71 insertions(+), 66 deletions(-) diff --git a/examples/0_MQTT_Gateway/fdrs_gateway_config.h b/examples/0_MQTT_Gateway/fdrs_gateway_config.h index ad7506f..82cce31 100644 --- a/examples/0_MQTT_Gateway/fdrs_gateway_config.h +++ b/examples/0_MQTT_Gateway/fdrs_gateway_config.h @@ -76,6 +76,8 @@ //#define LORA1_DELAY 1000 //#define LORA2_DELAY 1000 +#define PEER_TIMEOUT 300000 + // FastLED -- Not yet fully implemented //#define USE_LED #define LED_PIN 32 @@ -94,4 +96,4 @@ // MQTT Topics #define TOPIC_DATA "fdrs/data" #define TOPIC_STATUS "fdrs/status" -#define TOPIC_COMMAND "fdrs/command" +#define TOPIC_COMMAND "fdrs/command" \ No newline at end of file diff --git a/examples/1_UART_Gateway/1_UART_Gateway.ino b/examples/1_UART_Gateway/1_UART_Gateway.ino index ccbb31f..b4a18a4 100644 --- a/examples/1_UART_Gateway/1_UART_Gateway.ino +++ b/examples/1_UART_Gateway/1_UART_Gateway.ino @@ -97,8 +97,8 @@ void setup() { client.publish(TOPIC_STATUS, "FDRS initialized"); #endif } - void loop() { + handleCommands(); #ifdef ESPNOWG_DELAY if ((millis() - timeESPNOWG) >= ESPNOWG_DELAY) { timeESPNOWG = millis(); diff --git a/examples/1_UART_Gateway/fdrs_gateway_config.h b/examples/1_UART_Gateway/fdrs_gateway_config.h index 4ee35ff..bfc8168 100644 --- a/examples/1_UART_Gateway/fdrs_gateway_config.h +++ b/examples/1_UART_Gateway/fdrs_gateway_config.h @@ -11,8 +11,8 @@ // Current function options are: sendESPNOW(MAC), sendSerial(), sendMQTT(), // bufferLoRa(interface), bufferESPNOW(interface), bufferSerial(), and bufferMQTT(). -#define ESPNOWG_ACT sendSerial(); sendESPNOWpeers(); -#define SERIAL_ACT sendESPNOW(0x02); bufferLoRa(1); +#define ESPNOWG_ACT sendSerial(); +#define SERIAL_ACT sendESPNOW(0x02); bufferLoRa(1); sendESPNOWpeers(); #define MQTT_ACT #define LORAG_ACT sendSerial(); @@ -73,7 +73,7 @@ //#define SERIAL_DELAY 0 //#define MQTT_DELAY 0 //#define LORAG_DELAY 1000 -#define LORA1_DELAY 500 +//#define LORA1_DELAY 500 //#define LORA2_DELAY 1000 #define PEER_TIMEOUT 300000 diff --git a/examples/ESPNOW_Controller/ESPNOW_Controller.ino b/examples/ESPNOW_Controller/ESPNOW_Controller.ino index a44d594..3aa196e 100644 --- a/examples/ESPNOW_Controller/ESPNOW_Controller.ino +++ b/examples/ESPNOW_Controller/ESPNOW_Controller.ino @@ -9,42 +9,23 @@ #include "fdrs_sensor_config.h" #include -float data1; -float data2; - - - -void ctrl_1_cb(DataReading* theData) { - DBG("Controller1"); -} -void ctrl_2_cb(DataReading* theData) { - DBG("Controller2"); -} -void ctrl_3_cb(DataReading* theData) { - DBG("Controller3"); -} -void ctrl_4_cb(DataReading* theData) { - DBG("Controller4"); +void fdrs_recv_cb(DataReading theData) { + DBG("ID: " + String(theData.id)); + DBG("Type: " + String(theData.t)); + DBG("Data: " + String(theData.d)); } void setup() { beginFDRS(); - addFDRS(1000); - pingFDRS(1000); + //pingFDRS(1000); + addFDRS(1000, fdrs_recv_cb); + subscribeFDRS(READING_ID); } void loop() { -// data1 = readHum(); -// loadFDRS(data1, HUMIDITY_T); -// data2 = readTemp(); -// loadFDRS(data2, TEMP_T); -// sendFDRS(); -// sleepFDRS(10); //Sleep time in seconds -} - -float readTemp() { - return 22.069; -} - -float readHum() { - return random(0, 100); + // data1 = readHum(); + // loadFDRS(data1, HUMIDITY_T); + // data2 = readTemp(); + // loadFDRS(data2, TEMP_T); + // sendFDRS(); + // sleepFDRS(10); //Sleep time in seconds } diff --git a/examples/ESPNOW_Controller/fdrs_sensor_config.h b/examples/ESPNOW_Controller/fdrs_sensor_config.h index 7e02433..52af077 100644 --- a/examples/ESPNOW_Controller/fdrs_sensor_config.h +++ b/examples/ESPNOW_Controller/fdrs_sensor_config.h @@ -7,12 +7,6 @@ #define READING_ID 1 //Unique ID for this sensor #define GTWY_MAC 0x01 //Address of the nearest gateway -#define CONTROL_1 133 //Address for controller 1 -#define CONTROL_2 134 //Address for controller 2 -#define CONTROL_3 135 //Address for controller 3 -#define CONTROL_4 136 //Address for controller 4 - - #define USE_ESPNOW //#define USE_LORA //#define DEEP_SLEEP diff --git a/src/fdrs_functions.h b/src/fdrs_functions.h index dd61419..3aa7595 100644 --- a/src/fdrs_functions.h +++ b/src/fdrs_functions.h @@ -645,7 +645,7 @@ int findOpenPeer() { // Returns an expired entry in peer_list, -1 if full. DBG("No open peers"); return -1; } -int checkPeerExpired() { // Checks whether any entries in the peer_list have expired +int checkPeerExpired() { // Checks whether any entries in the peer_list have expired. Not currently used. for (int i = 0; i < 16; i++) { if ((millis() - peer_list[i].last_seen) >= PEER_TIMEOUT) { esp_now_del_peer(incMAC); diff --git a/src/fdrs_node.h b/src/fdrs_node.h index edd13bf..c510cc7 100644 --- a/src/fdrs_node.h +++ b/src/fdrs_node.h @@ -100,10 +100,9 @@ uint8_t data_count = 0; bool is_ping = false; bool is_added = false; uint32_t last_refresh; -void ctrl_1_cb(DataReading* reading); -void ctrl_2_cb(DataReading* reading); -void ctrl_3_cb(DataReading* reading); -void ctrl_4_cb(DataReading* reading); +void (*callback_ptr)(DataReading); +uint16_t subscription_list[256] = {}; +bool active_subs[256] = {}; // Set ESP-NOW send and receive callbacks for either ESP8266 or ESP32 #if defined(ESP8266) @@ -133,22 +132,15 @@ void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { } else{ memcpy(&incData, incomingData, len); int pkt_readings = len / sizeof(DataReading); - for (int i; i <= pkt_readings; i++) { //Cycle through array of incoming DataReadings for any addressed to this device - switch (incData[i].id) { - case CONTROL_1: - ctrl_1_cb(&incData[i]); - break; - case CONTROL_2: - ctrl_2_cb(&incData[i]); - break; - case CONTROL_3: - ctrl_3_cb(&incData[i]); - break; - case CONTROL_4: - ctrl_4_cb(&incData[i]); - break; + for (int i = 0; i <= pkt_readings; i++) { //Cycle through array of incoming DataReadings for any addressed to this device + for (int j = 0; j < 255; j++){ //Cycle through subscriptions for active entries + if (active_subs[j]){ + if (incData[i].id == subscription_list[j]){ + (*callback_ptr)(incData[i]); + } + } + } } - } } } @@ -468,17 +460,20 @@ bool seekFDRS(int timeout) { #endif } -bool addFDRS(int timeout) { +bool addFDRS(int timeout, void (*new_cb_ptr)(DataReading)) { + + callback_ptr = new_cb_ptr; + SystemPacket sys_packet = { .cmd = cmd_add, .param = 0 }; #ifdef USE_ESPNOW esp_now_send(gatewayAddress, (uint8_t *) &sys_packet, sizeof(SystemPacket)); - DBG("ESP-NOW peer subscription submitted to " + String(gatewayAddress[5])); + DBG("ESP-NOW peer registration request submitted to " + String(gatewayAddress[5])); uint32_t add_start = millis(); is_added = false; while ((millis() - add_start) <= timeout) { yield(); if (is_added) { - DBG("Subscription accepted. Timeout: " + String(gtwy_timeout)); + DBG("Registration accepted. Timeout: " + String(gtwy_timeout)); last_refresh = millis(); return true; } @@ -508,3 +503,36 @@ uint32_t pingFDRS(int timeout) { DBG(" LoRa ping not sent because it isn't implemented."); #endif } +bool subscribeFDRS(uint16_t sub_id){ + for(int i = 0; i < 255; i++){ + if ((subscription_list[i] == sub_id) && (active_subs[i])){ + DBG("You're already subscribed to that!"); + return true; + } + } + for(int i = 0; i < 255; i++){ + if (!active_subs[i]){ + DBG("Adding subscription at position " + String(i)); + subscription_list[i] = sub_id; + active_subs[i] = true; + return true; + + } + + } + DBG("No subscription could be established!"); + return false; + +} +bool unsubscribeFDRS(uint16_t sub_id){ + for(int i = 0; i < 255; i++){ + if ((subscription_list[i] == sub_id) && (active_subs[i])){ + DBG("Removing subscription."); + active_subs[i] = false; + return true; + } + } + DBG("No subscription to remove"); + return false; + +}