├── .gitignore ├── Controllers ├── FastLED │ ├── DataReading.h │ └── FastLED.ino ├── Irrigation │ └── Irrigation.ino ├── Lantern_8266 │ └── Lantern_8266.ino └── README.md ├── Examples ├── 1_LoRa_Sensor │ ├── 1_LoRa_Sensor.ino │ ├── fdrs_sensor.h │ └── sensor_config.h ├── 2_ESPNOW_Sensor │ ├── 2_ESPNOW_Sensor.ino │ ├── Sensor_setup.h │ └── fdrs_sensor.h ├── 3_ESPNOW_Gateway │ ├── 3_ESPNOW_Gateway.ino │ ├── fdrs_config.h │ └── fdrs_functions.h ├── 4_UART_Gateway │ ├── 4_UART_Gateway.ino │ ├── fdrs_config.h │ └── fdrs_functions.h ├── 5_MQTT_Gateway │ ├── 5_MQTT_Gateway.ino │ ├── fdrs_config.h │ └── fdrs_functions.h └── FDRS_Install │ ├── Instructions.txt │ └── fdrs_globals.h ├── FDRS_Gateway2000 ├── Advanced_Setup.png ├── Advanced_Setup_LoRa.png ├── Basic_LoRa_Setup.png ├── Basic_Setup.png ├── DataReading.h ├── FDRS_Gateway2000.ino ├── README.md ├── defaults.h ├── fdrs_config.h └── fdrs_functions.h ├── FDRS_Sensor2000 ├── FDRS_Sensor2000.ino └── fdrs_sensor.h ├── LICENSE ├── Original_FDRS ├── FDRS_Gateway │ ├── DataReading.h │ ├── FDRS_Gateway.ino │ └── fdrs_config.h ├── FDRS_Relay │ ├── FDRS_Relay.ino │ └── fdrs_config.h ├── FDRS_Terminal │ ├── DataReading.h │ ├── FDRS_Terminal.ino │ └── fdrs_config.h ├── Front-End_Modules │ └── FDRS_Blynk │ │ ├── DataReading.h │ │ └── FDRS_Blynk.ino └── README.md ├── README.md └── Sensors ├── AHT20_fdrs ├── AHT20_fdrs.ino └── fdrs_sensor.h ├── BME280_fdrs ├── BME280_fdrs.ino └── fdrs_sensor.h ├── BMP280_fdrs ├── BMP280_fdrs.ino └── fdrs_sensor.h ├── DHT22_8266 ├── DHT22_8266.ino └── DataReading.h ├── DS18B20_8266 ├── DS18B20_8266.ino └── DataReading.h ├── LilyGo_HiGrow_32 ├── DataReading.h └── LilyGo_HiGrow_32.ino ├── MESB_fdrs ├── MESB_fdrs.ino └── fdrs_sensor.h ├── MotionDetector ├── DataReading.h └── MotionDetector.ino ├── README.md └── TippingBucket ├── DataReading.h └── TippingBucket.ino /.gitignore: -------------------------------------------------------------------------------- 1 | Examples/FDRS_Install/fdrs_defaults.h 2 | Examples/FDRS_Install/fdrs_credentials.h 3 | -------------------------------------------------------------------------------- /Controllers/FastLED/DataReading.h: -------------------------------------------------------------------------------- 1 | 2 | typedef struct DataReading { 3 | float d; 4 | uint16_t id; 5 | uint8_t t; 6 | 7 | } DataReading; 8 | -------------------------------------------------------------------------------- /Controllers/FastLED/FastLED.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // FastLED Controller 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // Rest in Peace Dan Garcia, creator of FastLED. 7 | // 8 | #include 9 | #if defined(ESP8266) 10 | #include 11 | #include 12 | #elif defined(ESP32) 13 | #include 14 | #include 15 | #include 16 | 17 | #endif 18 | 19 | 20 | #define READING_ID 118 //Unique ID for controller 21 | #define GTWY_MAC 0x00 //Gateway MAC 22 | 23 | #define DATA_PIN 4 24 | #define BAT_ADC 33 25 | #define POWER_CTRL 5 26 | #define NUM_LEDS 24 27 | 28 | 29 | uint8_t broadcastAddress[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, GTWY_MAC}; 30 | CRGB leds[NUM_LEDS]; 31 | 32 | typedef struct DataReading { 33 | float d; 34 | uint16_t id; 35 | uint8_t t; 36 | 37 | } DataReading; 38 | 39 | DataReading theCommands[31]; 40 | int the_color = 0; 41 | int the_bright = 255; 42 | bool newData = false; 43 | int pkt_readings; 44 | int wait_time = 0; 45 | 46 | 47 | #if defined(ESP8266) 48 | void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { 49 | Serial.print("Last Packet Send Status: "); 50 | if (sendStatus == 0) { 51 | Serial.println("Delivery success"); 52 | } 53 | else { 54 | Serial.println("Delivery fail"); 55 | } 56 | } 57 | 58 | void OnDataRecv(uint8_t* mac, uint8_t *incomingData, uint8_t len) { 59 | #elif defined(ESP32) 60 | void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { 61 | Serial.print("Last Packet Send Status:"); 62 | Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail"); 63 | } 64 | void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { 65 | #endif 66 | memcpy(&theCommands, incomingData, len); 67 | pkt_readings = len / sizeof(DataReading); 68 | for (int i; i <= pkt_readings; i++) { //Cycle through array of incoming DataReadings for any addressed to this device 69 | if (theCommands[i].id == READING_ID) { 70 | if (theCommands[i].t == 201) { //Adjust color or brightness, depending on type. 71 | the_color = (int)theCommands[i].d; 72 | Serial.println("D:" + String(theCommands[i].d)); 73 | newData = true; 74 | } 75 | if (theCommands[i].t == 202) { 76 | the_bright = (int)theCommands[i].d; 77 | Serial.println("B:" + String(theCommands[i].d)); 78 | newData = true; 79 | } 80 | } 81 | } 82 | } 83 | 84 | float readBattery() 85 | { 86 | int vref = 1100; 87 | uint16_t volt = analogRead(BAT_ADC); 88 | Serial.println(volt); 89 | float battery_voltage = ((float)volt / 4095.0) * 2.0 * 3.3 * (vref); 90 | return battery_voltage; 91 | } 92 | 93 | void setup() { 94 | 95 | Serial.begin(115200); 96 | WiFi.mode(WIFI_STA); 97 | WiFi.disconnect(); 98 | #if defined(ESP8266) 99 | if (esp_now_init() != 0) { 100 | return; 101 | } 102 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 103 | esp_now_register_send_cb(OnDataSent); 104 | esp_now_register_recv_cb(OnDataRecv); 105 | 106 | // Register peer 107 | esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 108 | #elif defined(ESP32) 109 | if (esp_now_init() != ESP_OK) { 110 | Serial.println("Error initializing ESP-NOW"); 111 | return; 112 | } 113 | esp_now_register_send_cb(OnDataSent); 114 | esp_now_register_recv_cb(OnDataRecv); 115 | esp_now_peer_info_t peerInfo; 116 | peerInfo.channel = 0; 117 | peerInfo.encrypt = false; 118 | memcpy(peerInfo.peer_addr, broadcastAddress, 6); 119 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 120 | Serial.println("Failed to add peer"); 121 | return; 122 | } 123 | pinMode(POWER_CTRL, OUTPUT); 124 | digitalWrite(POWER_CTRL, 1); 125 | #endif 126 | 127 | Serial.println(); 128 | Serial.println("FARM DATA RELAY SYSTEM :: Pretty Lantern FastLED Module"); 129 | Serial.println("Thank You DAN GARCIA!"); 130 | Serial.println("MAC: " + WiFi.macAddress()); 131 | Serial.println("COLOR ID: " + String(COLOR_ID)); 132 | Serial.println("BRIGHT ID: " + String(BRIGHT_ID)); 133 | FastLED.addLeds(leds, NUM_LEDS); 134 | FastLED.show(); 135 | fill_solid(leds, NUM_LEDS, CRGB::Red); FastLED.show(); 136 | delay(250); 137 | fill_solid(leds, NUM_LEDS, CRGB::Blue); FastLED.show(); 138 | delay(250); 139 | fill_solid(leds, NUM_LEDS, CRGB::Green); FastLED.show(); 140 | delay(250); 141 | fill_solid(leds, NUM_LEDS, CRGB::Black); FastLED.show(); 142 | 143 | } 144 | 145 | void loop() 146 | { 147 | if (newData) { 148 | newData = false; 149 | fill_solid(leds, NUM_LEDS, CHSV(the_color, 255, the_bright)); FastLED.show(); 150 | } 151 | // if (millis() > wait_time) { 152 | // wait_time = wait_time + 30 * 1000; 153 | // DataReading theVoltage; 154 | // theVoltage.d = readBattery(); 155 | // theVoltage.id = BAT_ID; 156 | // theVoltage.t = 50; 157 | // esp_now_send(broadcastAddress, (uint8_t *) &theVoltage, sizeof(theVoltage)); 158 | // 159 | // 160 | // } 161 | } 162 | -------------------------------------------------------------------------------- /Controllers/Irrigation/Irrigation.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // Irrigation Controller 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // 7 | // 8 | #define CONTROL_1 133 //Address for controller 1 9 | #define CONTROL_2 134 //Address for controller 2 10 | #define CONTROL_3 135 //Address for controller 3 11 | #define CONTROL_4 136 //Address for controller 4 12 | #define GTWY_MAC 0x00 //Gateway MAC 13 | #define COIL_1 5 //Coil Pin 1 14 | #define COIL_2 4 //Coil Pin 2 15 | #define COIL_3 4 //Coil Pin 3 16 | #define COIL_4 4 //Coil Pin 4 17 | 18 | #include 19 | #if defined(ESP8266) 20 | #include 21 | #include 22 | #elif defined(ESP32) 23 | #include 24 | #include 25 | #include 26 | #endif 27 | 28 | uint8_t broadcastAddress[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, GTWY_MAC}; 29 | 30 | typedef struct DataReading { 31 | float d; 32 | uint16_t id; 33 | uint8_t t; 34 | 35 | } DataReading; 36 | 37 | const uint16_t espnow_size = 250 / sizeof(DataReading); 38 | DataReading theCommands[espnow_size]; 39 | 40 | int status_1 = 0; 41 | int status_2 = 0; 42 | int status_3 = 0; 43 | int status_4 = 0; 44 | 45 | bool newData = false; 46 | int pkt_readings; 47 | int wait_time = 0; 48 | 49 | #if defined(ESP8266) 50 | void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { 51 | Serial.print("Last Packet Send Status: "); 52 | if (sendStatus == 0) { 53 | Serial.println("Delivery success"); 54 | } 55 | else { 56 | Serial.println("Delivery fail"); 57 | } 58 | } 59 | 60 | void OnDataRecv(uint8_t* mac, uint8_t *incomingData, uint8_t len) { 61 | #elif defined(ESP32) 62 | void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { 63 | Serial.print("Last Packet Send Status:"); 64 | Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail"); 65 | } 66 | void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { 67 | #endif 68 | memcpy(&theCommands, incomingData, len); 69 | pkt_readings = len / sizeof(DataReading); 70 | for (int i; i <= pkt_readings; i++) { //Cycle through array of incoming DataReadings for any addressed to this device 71 | switch (theCommands[i].id) { 72 | case CONTROL_1: 73 | status_1 = (int)theCommands[i].d; 74 | Serial.println("D:" + String(theCommands[i].d)); 75 | newData = true; 76 | break; 77 | case CONTROL_2: 78 | status_2 = (int)theCommands[i].d; 79 | Serial.println("D:" + String(theCommands[i].d)); 80 | newData = true; 81 | break; 82 | case CONTROL_3: 83 | status_3 = (int)theCommands[i].d; 84 | Serial.println("D:" + String(theCommands[i].d)); 85 | newData = true; 86 | break; 87 | case CONTROL_4: 88 | status_4 = (int)theCommands[i].d; 89 | Serial.println("D:" + String(theCommands[i].d)); 90 | newData = true; 91 | break; 92 | } 93 | } 94 | } 95 | 96 | void setup() { 97 | Serial.begin(115200); 98 | WiFi.mode(WIFI_STA); 99 | WiFi.disconnect(); 100 | #if defined(ESP8266) 101 | if (esp_now_init() != 0) { 102 | return; 103 | } 104 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 105 | esp_now_register_send_cb(OnDataSent); 106 | esp_now_register_recv_cb(OnDataRecv); 107 | 108 | // Register peer 109 | esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 110 | #elif defined(ESP32) 111 | if (esp_now_init() != ESP_OK) { 112 | Serial.println("Error initializing ESP-NOW"); 113 | return; 114 | } 115 | esp_now_register_send_cb(OnDataSent); 116 | esp_now_register_recv_cb(OnDataRecv); 117 | esp_now_peer_info_t peerInfo; 118 | peerInfo.channel = 0; 119 | peerInfo.encrypt = false; 120 | memcpy(peerInfo.peer_addr, broadcastAddress, 6); 121 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 122 | Serial.println("Failed to add peer"); 123 | return; 124 | } 125 | #endif 126 | pinMode(5, OUTPUT); 127 | digitalWrite(COIL_1, LOW); 128 | pinMode(COIL_2, OUTPUT); 129 | digitalWrite(COIL_2, LOW); 130 | pinMode(COIL_3, OUTPUT); 131 | digitalWrite(COIL_3, LOW); 132 | pinMode(COIL_4, OUTPUT); 133 | digitalWrite(COIL_4, LOW); 134 | 135 | Serial.println(); 136 | Serial.println("FARM DATA RELAY SYSTEM :: Irrigation Module"); 137 | 138 | } 139 | void updateCoils() { 140 | if (status_1) { 141 | digitalWrite(COIL_1, HIGH); 142 | } else { 143 | digitalWrite(COIL_1, LOW); 144 | } 145 | if (status_2) { 146 | digitalWrite(COIL_2, HIGH); 147 | } else { 148 | digitalWrite(COIL_2, LOW); 149 | } 150 | if (status_3) { 151 | digitalWrite(COIL_3, HIGH); 152 | } else { 153 | digitalWrite(COIL_3, LOW); 154 | } 155 | if (status_4) { 156 | digitalWrite(COIL_4, HIGH); 157 | } else { 158 | digitalWrite(COIL_4, LOW); 159 | } 160 | } 161 | void loop() 162 | { 163 | if (newData) { 164 | newData = false; 165 | updateCoils(); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /Controllers/Lantern_8266/Lantern_8266.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // Pretty Lantern Controller 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // 7 | // 8 | 9 | #define READING_ID 72 //Unique ID (0 - 255) for controller 10 | #define GTWY_MAC 0x00 //Gateway MAC 11 | 12 | #define REDPIN 12 13 | #define GREENPIN 13 14 | #define BLUEPIN 15 15 | 16 | #include 17 | #include 18 | #include 19 | 20 | uint8_t broadcastAddress[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, GTWY_MAC}; 21 | 22 | typedef struct DataReading { 23 | float d; 24 | uint16_t id; 25 | uint8_t t; 26 | 27 | } DataReading; 28 | 29 | DataReading theCommands[31]; 30 | int the_color = 0; 31 | int the_bright = 255; 32 | bool newData = false; 33 | int pkt_readings; 34 | 35 | void showAnalogRGB( const CRGB& rgb) 36 | { 37 | analogWrite(REDPIN, rgb.r ); 38 | analogWrite(GREENPIN, rgb.g ); 39 | analogWrite(BLUEPIN, rgb.b ); 40 | } 41 | 42 | void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { 43 | Serial.print("Last Packet Send Status: "); 44 | if (sendStatus == 0) { 45 | Serial.println("Delivery success"); 46 | } 47 | else { 48 | Serial.println("Delivery fail"); 49 | } 50 | } 51 | 52 | void OnDataRecv(uint8_t* mac, uint8_t *incomingData, uint8_t len) { 53 | memcpy(&theCommands, incomingData, len); 54 | pkt_readings = len / sizeof(DataReading); 55 | for (int i; i <= pkt_readings; i++) { //Cycle through array of incoming DataReadings for any addressed to this device 56 | if (theCommands[i].id == READING_ID) { 57 | if (theCommands[i].t == 201) { //Adjust color or brightness, depending on type. 58 | the_color = (int)theCommands[i].d; 59 | Serial.println("D:" + String(theCommands[i].d)); 60 | newData = true; 61 | } 62 | if (theCommands[i].t == 202) { 63 | the_bright = (int)theCommands[i].d; 64 | Serial.println("B:" + String(theCommands[i].d)); 65 | newData = true; 66 | } 67 | } 68 | } 69 | } 70 | 71 | void colorBars() 72 | { 73 | showAnalogRGB( CRGB::Red ); delay(500); 74 | showAnalogRGB( CRGB::Green ); delay(500); 75 | showAnalogRGB( CRGB::Blue ); delay(500); 76 | showAnalogRGB( CRGB::Black ); delay(500); 77 | } 78 | 79 | void loop() 80 | { 81 | 82 | if (newData) { 83 | newData = false; 84 | showAnalogRGB(CHSV(the_color, 255, the_bright)); 85 | 86 | } 87 | 88 | } 89 | 90 | 91 | void setup() { 92 | pinMode(REDPIN, OUTPUT); 93 | pinMode(GREENPIN, OUTPUT); 94 | pinMode(BLUEPIN, OUTPUT); 95 | Serial.begin(115200); 96 | WiFi.mode(WIFI_STA); 97 | WiFi.disconnect(); 98 | if (esp_now_init() != 0) { 99 | return; 100 | } 101 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 102 | esp_now_register_send_cb(OnDataSent); 103 | esp_now_register_recv_cb(OnDataRecv); 104 | 105 | // Register peer 106 | esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 107 | Serial.println("FARM DATA RELAY SYSTEM :: Pretty Lantern Module"); 108 | Serial.println("MAC:" + WiFi.macAddress()); 109 | colorBars(); 110 | } 111 | -------------------------------------------------------------------------------- /Controllers/README.md: -------------------------------------------------------------------------------- 1 | # Controllers 2 | FDRS has some ability to communicate bi-directionally, meaning you can control water valves on relays or RGB lights with FastLED. This has always been a side-project and has a few shortcomings, so I don't include it in the main documentation. I do have a better method in mind, which I will roll out sometime before the 2022 holiday season. 3 | -------------------------------------------------------------------------------- /Examples/1_LoRa_Sensor/1_LoRa_Sensor.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // Basic Sensor Example 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // An example of how to send data using "fdrs_sensor.h". 7 | // 8 | 9 | 10 | #include "fdrs_sensor.h" 11 | 12 | float data1; 13 | float data2; 14 | 15 | void setup() { 16 | beginFDRS(); 17 | } 18 | void loop() { 19 | data1 = readHum(); 20 | loadFDRS(data1, HUMIDITY_T); 21 | data2 = readTemp(); 22 | loadFDRS(data2, TEMP_T); 23 | sendFDRS(); 24 | sleepFDRS(10); //Sleep time in seconds 25 | } 26 | 27 | float readTemp() { 28 | // return 42.069; 29 | return 42.5; 30 | } 31 | 32 | float readHum() { 33 | return 21.0345; 34 | } 35 | -------------------------------------------------------------------------------- /Examples/1_LoRa_Sensor/fdrs_sensor.h: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // "fdrs_sensor.h" 4 | // 5 | // Developed by Timm Bogner (timmbogner@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // 7 | #include "sensor_config.h" 8 | #if defined(ESP8266) 9 | #include 10 | #include 11 | #elif defined(ESP32) 12 | #include 13 | #include 14 | #include 15 | #endif 16 | 17 | #ifdef USE_LORA 18 | #include 19 | #endif 20 | 21 | #ifdef GLOBALS 22 | #define FDRS_BAND GLOBAL_BAND 23 | #define FDRS_SF GLOBAL_SF 24 | #else 25 | #define FDRS_BAND BAND 26 | #define FDRS_SF SF 27 | #endif 28 | 29 | #ifdef DEBUG 30 | #define DBG(a) (Serial.println(a)) 31 | #else 32 | #define DBG(a) 33 | #endif 34 | typedef struct __attribute__((packed)) DataReading { 35 | float d; 36 | uint16_t id; 37 | uint8_t t; 38 | 39 | } DataReading; 40 | 41 | const uint16_t espnow_size = 250 / sizeof(DataReading); 42 | uint8_t gatewayAddress[] = {MAC_PREFIX, GTWY_MAC}; 43 | uint8_t gtwyAddress[] = {gatewayAddress[3], gatewayAddress[4], GTWY_MAC}; 44 | 45 | 46 | uint32_t wait_time = 0; 47 | DataReading fdrsData[espnow_size]; 48 | uint8_t data_count = 0; 49 | 50 | void beginFDRS() { 51 | #ifdef DEBUG 52 | Serial.begin(115200); 53 | #endif 54 | DBG("FDRS Sensor ID " + String(READING_ID) + " initializing..."); 55 | DBG(" Gateway: " + String (GTWY_MAC, HEX)); 56 | #ifdef POWER_CTRL 57 | DBG("Powering up the sensor array!"); 58 | pinMode(POWER_CTRL, OUTPUT); 59 | digitalWrite(POWER_CTRL, 1); 60 | #endif 61 | // Init ESP-NOW for either ESP8266 or ESP32 and set MAC address 62 | #ifdef USE_ESPNOW 63 | DBG("Initializing ESP-NOW!"); 64 | WiFi.mode(WIFI_STA); 65 | WiFi.disconnect(); 66 | #if defined(ESP8266) 67 | if (esp_now_init() != 0) { 68 | return; 69 | } 70 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 71 | // Register peers 72 | esp_now_add_peer(gatewayAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 73 | #elif defined(ESP32) 74 | if (esp_now_init() != ESP_OK) { 75 | DBG("Error initializing ESP-NOW"); 76 | return; 77 | } 78 | esp_now_peer_info_t peerInfo; 79 | peerInfo.ifidx = WIFI_IF_STA; 80 | peerInfo.channel = 0; 81 | peerInfo.encrypt = false; 82 | // Register first peer 83 | memcpy(peerInfo.peer_addr, gatewayAddress, 6); 84 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 85 | DBG("Failed to add peer"); 86 | return; 87 | } 88 | #endif 89 | DBG(" ESP-NOW Initialized."); 90 | #endif 91 | #ifdef USE_LORA 92 | DBG("Initializing LoRa!"); 93 | DBG(FDRS_BAND); 94 | DBG(SF); 95 | #ifndef __AVR__ 96 | SPI.begin(SCK, MISO, MOSI, SS); 97 | #endif 98 | LoRa.setPins(SS, RST, DIO0); 99 | if (!LoRa.begin(FDRS_BAND)) { 100 | while (1); 101 | } 102 | LoRa.setSpreadingFactor(FDRS_SF); 103 | DBG(" LoRa Initialized."); 104 | #endif 105 | } 106 | void transmitLoRa(uint8_t* mac, DataReading * packet, uint8_t len) { 107 | #ifdef USE_LORA 108 | uint8_t pkt[5 + (len * sizeof(DataReading))]; 109 | memcpy(&pkt, mac, 3); // 110 | memcpy(&pkt[3], &LoRaAddress, 2); 111 | memcpy(&pkt[5], packet, len * sizeof(DataReading)); 112 | LoRa.beginPacket(); 113 | LoRa.write((uint8_t*)&pkt, sizeof(pkt)); 114 | LoRa.endPacket(); 115 | #endif 116 | } 117 | void sendFDRS() { 118 | DBG("Sending FDRS Packet!"); 119 | #ifdef USE_ESPNOW 120 | esp_now_send(gatewayAddress, (uint8_t *) &fdrsData, data_count * sizeof(DataReading)); 121 | delay(5); 122 | DBG(" ESP-NOW sent."); 123 | #endif 124 | #ifdef USE_LORA 125 | transmitLoRa(gtwyAddress, fdrsData, data_count); 126 | DBG(" LoRa sent."); 127 | #endif 128 | data_count = 0; 129 | } 130 | void loadFDRS(float d, uint8_t t) { 131 | DBG("Data loaded. Type: " + String(t)); 132 | if (data_count > espnow_size) sendFDRS(); 133 | DataReading dr; 134 | dr.id = READING_ID; 135 | dr.t = t; 136 | dr.d = d; 137 | fdrsData[data_count] = dr; 138 | data_count++; 139 | } 140 | void sleepFDRS(int sleep_time) { 141 | DBG("Sleepytime!"); 142 | #ifdef DEEP_SLEEP 143 | DBG(" Deep sleeping."); 144 | #ifdef ESP32 145 | esp_sleep_enable_timer_wakeup(sleep_time * 1000000); 146 | esp_deep_sleep_start(); 147 | #endif 148 | #ifdef ESP8266 149 | ESP.deepSleep(sleep_time * 1000000); 150 | #endif 151 | #endif 152 | DBG(" Delaying."); 153 | delay(sleep_time * 1000); 154 | } 155 | -------------------------------------------------------------------------------- /Examples/1_LoRa_Sensor/sensor_config.h: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // Sensor Configuration 4 | // 5 | #include //Uncomment if you install the globals file 6 | 7 | #define DEBUG 8 | 9 | #define READING_ID 1 //Unique ID for this sensor 10 | #define GTWY_MAC 0x04 //Address of the nearest gateway 11 | 12 | //#define USE_ESPNOW 13 | #define USE_LORA 14 | #define DEEP_SLEEP 15 | //#define POWER_CTRL 14 16 | 17 | 18 | //LoRa Configuration 19 | #define SCK 5 20 | #define MISO 19 21 | #define MOSI 27 22 | #define SS 18 23 | #define RST 14 24 | #define DIO0 26 25 | //433E6 for Asia 26 | //866E6 for Europe 27 | //915E6 for North America 28 | #define BAND 915E6 29 | #define SF 7 30 | -------------------------------------------------------------------------------- /Examples/2_ESPNOW_Sensor/2_ESPNOW_Sensor.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // Basic Sensor Example 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // An example of how to send data using "fdrs_sensor.h". 7 | // 8 | 9 | #define DEBUG 10 | #define CREDENTIALS 11 | 12 | #include 13 | #include "fdrs_sensor.h" 14 | 15 | float data1; 16 | float data2; 17 | 18 | void setup() { 19 | beginFDRS(); 20 | } 21 | void loop() { 22 | data1 = readHum(); 23 | loadFDRS(data1, HUMIDITY_T); 24 | data2 = readTemp(); 25 | loadFDRS(data2, TEMP_T); 26 | sendFDRS(); 27 | sleepFDRS(10); //Sleep time in seconds 28 | } 29 | 30 | float readTemp() { 31 | return 42.069; 32 | } 33 | 34 | float readHum() { 35 | return 21.0345; 36 | } 37 | -------------------------------------------------------------------------------- /Examples/2_ESPNOW_Sensor/Sensor_setup.h: -------------------------------------------------------------------------------- 1 | #define READING_ID 2 //Unique ID for this sensor 2 | #define GTWY_MAC 0x03 //Address of the nearest gateway 3 | 4 | #define USE_ESPNOW 5 | //#define USE_LORA 6 | #define DEEP_SLEEP 7 | //#define POWER_CTRL 14 8 | //#define MAC_PREFIX 0xAA, 0xBB, 0xCC, 0xDD, 0xEE 9 | 10 | //LoRa Configuration 11 | #define SCK 5 12 | #define MISO 19 13 | #define MOSI 27 14 | #define SS 18 15 | #define RST 14 16 | #define DIO0 26 17 | -------------------------------------------------------------------------------- /Examples/2_ESPNOW_Sensor/fdrs_sensor.h: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // "fdrs_sensor.h" 4 | // 5 | // Developed by Timm Bogner (timmbogner@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // 7 | #include "sensor_setup.h" 8 | 9 | #if defined(ESP8266) 10 | #include 11 | #include 12 | #elif defined(ESP32) 13 | #include 14 | #include 15 | #include 16 | #endif 17 | 18 | #ifdef USE_LORA 19 | #include 20 | #endif 21 | 22 | #ifdef DEBUG 23 | #define DBG(a) (Serial.println(a)) 24 | #else 25 | #define DBG(a) 26 | #endif 27 | 28 | typedef struct __attribute__((packed)) DataReading { 29 | float d; 30 | uint16_t id; 31 | uint8_t t; 32 | 33 | } DataReading; 34 | 35 | const uint16_t espnow_size = 250 / sizeof(DataReading); 36 | uint8_t gatewayAddress[] = {MAC_PREFIX, GTWY_MAC}; 37 | uint8_t gtwyAddress[] = {gatewayAddress[3], gatewayAddress[4], GTWY_MAC}; 38 | 39 | 40 | uint32_t wait_time = 0; 41 | DataReading fdrsData[espnow_size]; 42 | uint8_t data_count = 0; 43 | 44 | void beginFDRS() { 45 | #ifdef DEBUG 46 | Serial.begin(115200); 47 | #endif 48 | DBG("FDRS Sensor ID " + String(READING_ID)+ " initializing..."); 49 | DBG(" Gateway: " + String (GTWY_MAC, HEX)); 50 | #ifdef POWER_CTRL 51 | DBG("Powering up the sensor array!"); 52 | pinMode(POWER_CTRL, OUTPUT); 53 | digitalWrite(POWER_CTRL, 1); 54 | #endif 55 | // Init ESP-NOW for either ESP8266 or ESP32 and set MAC address 56 | #ifdef USE_ESPNOW 57 | DBG("Initializing ESP-NOW!"); 58 | WiFi.mode(WIFI_STA); 59 | WiFi.disconnect(); 60 | #if defined(ESP8266) 61 | if (esp_now_init() != 0) { 62 | return; 63 | } 64 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 65 | // Register peers 66 | esp_now_add_peer(gatewayAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 67 | #elif defined(ESP32) 68 | if (esp_now_init() != ESP_OK) { 69 | DBG("Error initializing ESP-NOW"); 70 | return; 71 | } 72 | esp_now_peer_info_t peerInfo; 73 | peerInfo.ifidx = WIFI_IF_STA; 74 | peerInfo.channel = 0; 75 | peerInfo.encrypt = false; 76 | // Register first peer 77 | memcpy(peerInfo.peer_addr, gatewayAddress, 6); 78 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 79 | DBG("Failed to add peer"); 80 | return; 81 | } 82 | #endif 83 | DBG(" ESP-NOW Initialized."); 84 | #endif 85 | #ifdef USE_LORA 86 | DBG("Initializing LoRa!"); 87 | DBG(BAND); 88 | DBG(SF); 89 | #ifndef __AVR__ 90 | SPI.begin(SCK, MISO, MOSI, SS); 91 | #endif 92 | LoRa.setPins(SS, RST, DIO0); 93 | if (!LoRa.begin(BAND)) { 94 | while (1); 95 | } 96 | LoRa.setSpreadingFactor(SF); 97 | DBG(" LoRa Initialized."); 98 | #endif 99 | } 100 | void transmitLoRa(uint8_t* mac, DataReading * packet, uint8_t len) { 101 | #ifdef USE_LORA 102 | uint8_t pkt[5 + (len * sizeof(DataReading))]; 103 | memcpy(&pkt, mac, 3); 104 | memcpy(&pkt[3], &LoRaAddress, 2); 105 | memcpy(&pkt[5], packet, len * sizeof(DataReading)); 106 | LoRa.beginPacket(); 107 | LoRa.write((uint8_t*)&pkt, sizeof(pkt)); 108 | LoRa.endPacket(); 109 | #endif 110 | } 111 | void sendFDRS() { 112 | DBG("Sending FDRS Packet!"); 113 | #ifdef USE_ESPNOW 114 | esp_now_send(gatewayAddress, (uint8_t *) &fdrsData, data_count * sizeof(DataReading)); 115 | delay(5); 116 | DBG(" ESP-NOW sent."); 117 | #endif 118 | #ifdef USE_LORA 119 | transmitLoRa(gtwyAddress, fdrsData, data_count); 120 | DBG(" LoRa sent."); 121 | #endif 122 | data_count = 0; 123 | } 124 | void loadFDRS(float d, uint8_t t) { 125 | DBG("Data loaded. Type: " + String(t)); 126 | if (data_count > espnow_size) sendFDRS(); 127 | DataReading dr; 128 | dr.id = READING_ID; 129 | dr.t = t; 130 | dr.d = d; 131 | fdrsData[data_count] = dr; 132 | data_count++; 133 | } 134 | void sleepFDRS(int sleep_time) { 135 | DBG("Sleepytime!"); 136 | #ifdef DEEP_SLEEP 137 | DBG(" Deep sleeping."); 138 | #ifdef ESP32 139 | esp_sleep_enable_timer_wakeup(sleep_time * 1000000); 140 | esp_deep_sleep_start(); 141 | #endif 142 | #ifdef ESP8266 143 | ESP.deepSleep(sleep_time * 1000000); 144 | #endif 145 | #endif 146 | DBG(" Delaying."); 147 | delay(sleep_time * 1000); 148 | } 149 | -------------------------------------------------------------------------------- /Examples/3_ESPNOW_Gateway/3_ESPNOW_Gateway.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // GATEWAY 2.000 4 | // 5 | // Developed by Timm Bogner (timmbogner@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // 7 | 8 | #define DEBUG 9 | #define CREDENTIALS 10 | 11 | #include 12 | #include "fdrs_config.h" 13 | 14 | #ifdef ESP8266 15 | #include 16 | #include 17 | #elif defined(ESP32) 18 | #include 19 | #include 20 | #include 21 | #endif 22 | #include 23 | #ifdef USE_WIFI 24 | #include 25 | #endif 26 | #ifdef USE_LORA 27 | #include 28 | #endif 29 | #ifdef USE_LED 30 | #include 31 | #endif 32 | #include "fdrs_functions.h" 33 | 34 | void setup() { 35 | #if defined(ESP8266) 36 | Serial.begin(115200); 37 | #elif defined(ESP32) 38 | Serial.begin(115200); 39 | UART_IF.begin(115200, SERIAL_8N1, RXD2, TXD2); 40 | #endif 41 | DBG("Address:" + String (UNIT_MAC, HEX)); 42 | #ifdef USE_LED 43 | FastLED.addLeds(leds, NUM_LEDS); 44 | leds[0] = CRGB::Blue; 45 | FastLED.show(); 46 | #endif 47 | #ifdef USE_WIFI 48 | delay(10); 49 | WiFi.begin(ssid, password); 50 | while (WiFi.status() != WL_CONNECTED) { 51 | DBG("Connecting to WiFi..."); 52 | delay(500); 53 | } 54 | DBG("WiFi Connected"); 55 | client.setServer(mqtt_server, 1883); 56 | if (!client.connected()) { 57 | DBG("Connecting MQTT..."); 58 | reconnect(); 59 | } 60 | DBG("MQTT Connected"); 61 | client.setCallback(mqtt_callback); 62 | #else 63 | begin_espnow(); 64 | #endif 65 | #ifdef USE_LORA 66 | DBG("Initializing LoRa!"); 67 | SPI.begin(SCK, MISO, MOSI, SS); 68 | LoRa.setPins(SS, RST, DIO0); 69 | if (!LoRa.begin(BAND)) { 70 | while (1); 71 | } 72 | DBG(" LoRa initialized."); 73 | #endif 74 | 75 | //DBG(sizeof(DataReading)); 76 | 77 | } 78 | 79 | void loop() { 80 | if (millis() > timeESPNOWG) { 81 | timeESPNOWG += ESPNOWG_DELAY; 82 | if (lenESPNOWG > 0) releaseESPNOW(0); 83 | } 84 | if (millis() > timeESPNOW1) { 85 | timeESPNOW1 += ESPNOW1_DELAY; 86 | if (lenESPNOW1 > 0) releaseESPNOW(1); 87 | } 88 | if (millis() > timeESPNOW2) { 89 | timeESPNOW2 += ESPNOW2_DELAY; 90 | if (lenESPNOW2 > 0) releaseESPNOW(2); 91 | } 92 | if (millis() > timeSERIAL) { 93 | timeSERIAL += SERIAL_DELAY; 94 | if (lenSERIAL > 0) releaseSerial(); 95 | } 96 | if (millis() > timeMQTT) { 97 | timeMQTT += MQTT_DELAY; 98 | if (lenMQTT > 0) releaseMQTT(); 99 | } 100 | if (millis() > timeLORAG) { 101 | timeLORAG += LORAG_DELAY; 102 | if (lenLORAG > 0) releaseLoRa(0); 103 | } 104 | if (millis() > timeLORA1) { 105 | timeLORA1 += LORA1_DELAY; 106 | if (lenLORA1 > 0) releaseLoRa(1); 107 | } 108 | if (millis() > timeLORA2) { 109 | timeLORA2 += LORA2_DELAY; 110 | if (lenLORA2 > 0) releaseLoRa(2); 111 | } 112 | 113 | while (UART_IF.available()) { 114 | getSerial(); 115 | } 116 | getLoRa(); 117 | #ifdef USE_WIFI 118 | if (!client.connected()) { 119 | DBG("Connecting MQTT..."); 120 | reconnect(); 121 | } 122 | client.loop(); 123 | #endif 124 | if (newData) { 125 | switch (newData) { 126 | case 1: //ESP-NOW #1 127 | ESPNOW1_ACT 128 | break; 129 | case 2: //ESP-NOW #2 130 | ESPNOW2_ACT 131 | break; 132 | case 3: //ESP-NOW General 133 | ESPNOWG_ACT 134 | break; 135 | case 4: //Serial 136 | SERIAL_ACT 137 | break; 138 | case 5: //MQTT 139 | MQTT_ACT 140 | break; 141 | case 6: //LoRa General 142 | LORAG_ACT 143 | break; 144 | case 7: //LoRa #1 145 | LORA1_ACT 146 | break; 147 | case 8: //LoRa #2 148 | LORA2_ACT 149 | break; 150 | } 151 | newData = 0; 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Examples/3_ESPNOW_Gateway/fdrs_config.h: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // GATEWAY 2.000 Configuration 4 | 5 | #define UNIT_MAC 0x03 // The address of this gateway 6 | #define ESPNOW1_PEER 0xFD // ESPNOW1 Address 7 | #define ESPNOW2_PEER 0xFE // ESPNOW2 Address 8 | #define LORA1_PEER 0xFD // LoRa1 Address 9 | #define LORA2_PEER 0xFE // LoRa2 Address 10 | 11 | //Actions -- Define what happens when a packet arrives at each interface: 12 | //Current function options are: sendESPNOW(MAC), sendSerial(), sendMQTT(), bufferESPNOW(interface), bufferSerial(), and bufferLoRa(interface). 13 | 14 | #define ESPNOWG_ACT sendESPNOW(0x04); 15 | #define SERIAL_ACT 16 | #define MQTT_ACT 17 | #define LORAG_ACT 18 | #define ESPNOW1_ACT 19 | #define ESPNOW2_ACT 20 | #define LORA1_ACT 21 | #define LORA2_ACT 22 | 23 | //#define USE_LORA 24 | //#define USE_WIFI //Used only for MQTT gateway 25 | 26 | 27 | #if defined (ESP32) 28 | #define RXD2 14 29 | #define TXD2 15 30 | #define UART_IF Serial2 31 | #else 32 | #define UART_IF Serial 33 | #endif 34 | 35 | ////LoRa Configuration -- Needed only if using LoRa 36 | #define SCK 5 37 | #define MISO 19 38 | #define MOSI 27 39 | #define SS 18 40 | #define RST 14 41 | #define DIO0 26 42 | 43 | // Buffer Delays - in milliseconds 44 | #define ESPNOW1_DELAY 0 45 | #define ESPNOW2_DELAY 0 46 | #define ESPNOWG_DELAY 0 47 | #define SERIAL_DELAY 0 48 | #define MQTT_DELAY 0 49 | #define LORAG_DELAY 1000 50 | #define LORA1_DELAY 1000 51 | #define LORA2_DELAY 1000 52 | -------------------------------------------------------------------------------- /Examples/3_ESPNOW_Gateway/fdrs_functions.h: -------------------------------------------------------------------------------- 1 | #ifdef DEBUG 2 | #define DBG(a) (Serial.println(a)) 3 | #else 4 | #define DBG(a) 5 | #endif 6 | 7 | #ifdef GLOBALS 8 | #define FDRS_BAND GLOBAL_BAND 9 | #define FDRS_SF GLOBAL_SF 10 | #else 11 | #define FDRS_BAND BAND 12 | #define FDRS_SF SF 13 | #endif 14 | 15 | typedef struct __attribute__((packed)) DataReading { 16 | float d; 17 | uint16_t id; 18 | uint8_t t; 19 | 20 | } DataReading; 21 | 22 | const uint8_t espnow_size = 250 / sizeof(DataReading); 23 | const uint8_t lora_size = 256 / sizeof(DataReading); 24 | const uint8_t mac_prefix[] = {MAC_PREFIX}; 25 | #if defined (ESP32) 26 | esp_now_peer_info_t peerInfo; 27 | #endif 28 | 29 | uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 30 | uint8_t selfAddress[] = {MAC_PREFIX, UNIT_MAC}; 31 | uint8_t ESPNOW1[] = {MAC_PREFIX, ESPNOW1_PEER}; 32 | uint8_t ESPNOW2[] = {MAC_PREFIX, ESPNOW2_PEER}; 33 | uint8_t incMAC[6]; 34 | uint8_t LoRa1[] = {mac_prefix[3], mac_prefix[4], LORA1_PEER}; 35 | uint8_t LoRa2[] = {mac_prefix[3], mac_prefix[4], LORA2_PEER}; 36 | 37 | DataReading theData[256]; 38 | uint8_t ln; 39 | uint8_t newData = 0; 40 | 41 | DataReading ESPNOW1buffer[256]; 42 | uint8_t lenESPNOW1 = 0; 43 | uint32_t timeESPNOW1 = 0; 44 | DataReading ESPNOW2buffer[256]; 45 | uint8_t lenESPNOW2 = 0; 46 | uint32_t timeESPNOW2 = 0; 47 | DataReading ESPNOWGbuffer[256]; 48 | uint8_t lenESPNOWG = 0; 49 | uint32_t timeESPNOWG = 0; 50 | DataReading SERIALbuffer[256]; 51 | uint8_t lenSERIAL = 0; 52 | uint32_t timeSERIAL = 0; 53 | DataReading MQTTbuffer[256]; 54 | uint8_t lenMQTT = 0; 55 | uint32_t timeMQTT = 0; 56 | DataReading LORAGbuffer[256]; 57 | uint8_t lenLORAG = 0; 58 | uint32_t timeLORAG = 0; 59 | DataReading LORA1buffer[256]; 60 | uint8_t lenLORA1 = 0; 61 | uint32_t timeLORA1 = 0; 62 | DataReading LORA2buffer[256]; 63 | uint8_t lenLORA2 = 0; 64 | uint32_t timeLORA2 = 0; 65 | 66 | WiFiClient espClient; 67 | #ifdef USE_LED 68 | CRGB leds[NUM_LEDS]; 69 | #endif 70 | #ifdef USE_WIFI 71 | PubSubClient client(espClient); 72 | const char* ssid = WIFI_NET; 73 | const char* password = WIFI_PASS; 74 | const char* mqtt_server = MQTT_ADDR; 75 | #endif 76 | 77 | 78 | // Set ESP-NOW send and receive callbacks for either ESP8266 or ESP32 79 | #if defined(ESP8266) 80 | void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { 81 | } 82 | void OnDataRecv(uint8_t* mac, uint8_t *incomingData, uint8_t len) { 83 | #elif defined(ESP32) 84 | void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { 85 | } 86 | void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { 87 | #endif 88 | memcpy(&theData, incomingData, sizeof(theData)); 89 | memcpy(&incMAC, mac, sizeof(incMAC)); 90 | if (memcmp(&incMAC, &ESPNOW1, 6) == 0) newData = 1; 91 | else if (memcmp(&incMAC, &ESPNOW2, 6) == 0) newData = 2; 92 | else newData = 3; 93 | ln = len / sizeof(DataReading); 94 | DBG("Incoming ESP-NOW."); 95 | } 96 | void getSerial() { 97 | String incomingString = UART_IF.readStringUntil('\n'); 98 | DynamicJsonDocument doc(24576); 99 | DeserializationError error = deserializeJson(doc, incomingString); 100 | if (error) { // Test if parsing succeeds. 101 | // DBG("json parse err"); 102 | // DBG(incomingString); 103 | return; 104 | } else { 105 | int s = doc.size(); 106 | //UART_IF.println(s); 107 | for (int i = 0; i < s; i++) { 108 | theData[i].id = doc[i]["id"]; 109 | theData[i].t = doc[i]["type"]; 110 | theData[i].d = doc[i]["data"]; 111 | } 112 | ln = s; 113 | newData = 4; 114 | DBG("Incoming Serial."); 115 | 116 | } 117 | } 118 | void mqtt_callback(char* topic, byte * message, unsigned int length) { 119 | String incomingString; 120 | for (int i = 0; i < length; i++) { 121 | incomingString += (char)message[i]; 122 | } 123 | StaticJsonDocument<2048> doc; 124 | DeserializationError error = deserializeJson(doc, incomingString); 125 | if (error) { // Test if parsing succeeds. 126 | DBG("json parse err"); 127 | DBG(incomingString); 128 | return; 129 | } else { 130 | int s = doc.size(); 131 | //UART_IF.println(s); 132 | for (int i = 0; i < s; i++) { 133 | theData[i].id = doc[i]["id"]; 134 | theData[i].t = doc[i]["type"]; 135 | theData[i].d = doc[i]["data"]; 136 | } 137 | ln = s; 138 | newData = 5; 139 | DBG("Incoming MQTT."); 140 | 141 | } 142 | } 143 | 144 | void getLoRa() { 145 | #ifdef USE_LORA 146 | int packetSize = LoRa.parsePacket(); 147 | if (packetSize) { 148 | uint8_t packet[packetSize]; 149 | uint8_t incLORAMAC[2]; 150 | LoRa.readBytes((uint8_t *)&packet, packetSize); 151 | // for (int i = 0; i < packetSize; i++) { 152 | // UART_IF.println(packet[i], HEX); 153 | // } 154 | if (memcmp(&packet, &selfAddress[3], 3) == 0) { //Check if addressed to this device 155 | memcpy(&incLORAMAC, &packet[3], 2); //Split off address portion of packet 156 | memcpy(&theData, &packet[5], packetSize - 5); //Split off data portion of packet 157 | if (memcmp(&incLORAMAC, &LoRa1, 2) == 0) newData = 7; //Check if it is from a registered sender 158 | else if (memcmp(&incLORAMAC, &LoRa2, 2) == 0) newData = 8; 159 | else newData = 6; 160 | ln = (packetSize - 5) / sizeof(DataReading); 161 | newData = 6; 162 | DBG("Incoming LoRa."); 163 | 164 | } 165 | } 166 | #endif 167 | } 168 | 169 | void sendESPNOW(uint8_t address) { 170 | DBG("Sending ESP-NOW."); 171 | uint8_t NEWPEER[] = {MAC_PREFIX, address}; 172 | #if defined(ESP32) 173 | esp_now_peer_info_t peerInfo; 174 | peerInfo.ifidx = WIFI_IF_STA; 175 | peerInfo.channel = 0; 176 | peerInfo.encrypt = false; 177 | memcpy(peerInfo.peer_addr, NEWPEER, 6); 178 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 179 | DBG("Failed to add peer"); 180 | return; 181 | } 182 | #endif 183 | 184 | DataReading thePacket[ln]; 185 | int j = 0; 186 | for (int i = 0; i < ln; i++) { 187 | if ( j > espnow_size) { 188 | j = 0; 189 | esp_now_send(NEWPEER, (uint8_t *) &thePacket, sizeof(thePacket)); 190 | } 191 | thePacket[j] = theData[i]; 192 | j++; 193 | } 194 | esp_now_send(NEWPEER, (uint8_t *) &thePacket, j * sizeof(DataReading)); 195 | esp_now_del_peer(NEWPEER); 196 | } 197 | 198 | void sendSerial() { 199 | DBG("Sending Serial."); 200 | DynamicJsonDocument doc(24576); 201 | for (int i = 0; i < ln; i++) { 202 | doc[i]["id"] = theData[i].id; 203 | doc[i]["type"] = theData[i].t; 204 | doc[i]["data"] = theData[i].d; 205 | } 206 | serializeJson(doc, UART_IF); 207 | UART_IF.println(); 208 | 209 | #ifndef ESP8266 210 | serializeJson(doc, Serial); 211 | Serial.println(); 212 | #endif 213 | 214 | } 215 | void sendMQTT() { 216 | #ifdef USE_WIFI 217 | DBG("Sending MQTT."); 218 | DynamicJsonDocument doc(24576); 219 | for (int i = 0; i < ln; i++) { 220 | doc[i]["id"] = theData[i].id; 221 | doc[i]["type"] = theData[i].t; 222 | doc[i]["data"] = theData[i].d; 223 | } 224 | String outgoingString; 225 | serializeJson(doc, outgoingString); 226 | client.publish("esp/fdrs", (char*) outgoingString.c_str()); 227 | #endif 228 | } 229 | 230 | void bufferESPNOW(uint8_t interface) { 231 | DBG("Buffering ESP-NOW."); 232 | 233 | switch (interface) { 234 | case 0: 235 | for (int i = 0; i < ln; i++) { 236 | ESPNOWGbuffer[lenESPNOWG + i] = theData[i]; 237 | } 238 | lenESPNOWG += ln; 239 | break; 240 | case 1: 241 | for (int i = 0; i < ln; i++) { 242 | ESPNOW1buffer[lenESPNOW1 + i] = theData[i]; 243 | } 244 | lenESPNOW1 += ln; 245 | break; 246 | case 2: 247 | for (int i = 0; i < ln; i++) { 248 | ESPNOW2buffer[lenESPNOW2 + i] = theData[i]; 249 | } 250 | lenESPNOW2 += ln; 251 | break; 252 | } 253 | } 254 | void bufferSerial() { 255 | DBG("Buffering Serial."); 256 | for (int i = 0; i < ln; i++) { 257 | SERIALbuffer[lenSERIAL + i] = theData[i]; 258 | } 259 | lenSERIAL += ln; 260 | //UART_IF.println("SENDSERIAL:" + String(lenSERIAL) + " "); 261 | } 262 | void bufferMQTT() { 263 | DBG("Buffering MQTT."); 264 | for (int i = 0; i < ln; i++) { 265 | MQTTbuffer[lenMQTT + i] = theData[i]; 266 | } 267 | lenMQTT += ln; 268 | } 269 | //void bufferLoRa() { 270 | // for (int i = 0; i < ln; i++) { 271 | // LORAbuffer[lenLORA + i] = theData[i]; 272 | // } 273 | // lenLORA += ln; 274 | //} 275 | void bufferLoRa(uint8_t interface) { 276 | DBG("Buffering LoRa."); 277 | switch (interface) { 278 | case 0: 279 | for (int i = 0; i < ln; i++) { 280 | LORAGbuffer[lenLORAG + i] = theData[i]; 281 | } 282 | lenLORAG += ln; 283 | break; 284 | case 1: 285 | for (int i = 0; i < ln; i++) { 286 | LORA1buffer[lenLORA1 + i] = theData[i]; 287 | } 288 | lenLORA1 += ln; 289 | break; 290 | case 2: 291 | for (int i = 0; i < ln; i++) { 292 | LORA2buffer[lenLORA2 + i] = theData[i]; 293 | } 294 | lenLORA2 += ln; 295 | break; 296 | } 297 | } 298 | 299 | void releaseESPNOW(uint8_t interface) { 300 | DBG("Releasing ESP-NOW."); 301 | switch (interface) { 302 | case 0: 303 | { 304 | DataReading thePacket[espnow_size]; 305 | int j = 0; 306 | for (int i = 0; i < lenESPNOWG; i++) { 307 | if ( j > espnow_size) { 308 | j = 0; 309 | esp_now_send(broadcast_mac, (uint8_t *) &thePacket, sizeof(thePacket)); 310 | } 311 | thePacket[j] = ESPNOWGbuffer[i]; 312 | j++; 313 | } 314 | esp_now_send(broadcast_mac, (uint8_t *) &thePacket, j * sizeof(DataReading)); 315 | lenESPNOWG = 0; 316 | break; 317 | } 318 | case 1: 319 | { 320 | DataReading thePacket[espnow_size]; 321 | int j = 0; 322 | for (int i = 0; i < lenESPNOW1; i++) { 323 | if ( j > espnow_size) { 324 | j = 0; 325 | esp_now_send(ESPNOW1, (uint8_t *) &thePacket, sizeof(thePacket)); 326 | } 327 | thePacket[j] = ESPNOW1buffer[i]; 328 | j++; 329 | } 330 | esp_now_send(ESPNOW1, (uint8_t *) &thePacket, j * sizeof(DataReading)); 331 | lenESPNOW1 = 0; 332 | break; 333 | } 334 | case 2: 335 | { 336 | DataReading thePacket[espnow_size]; 337 | int j = 0; 338 | for (int i = 0; i < lenESPNOW2; i++) { 339 | if ( j > espnow_size) { 340 | j = 0; 341 | esp_now_send(ESPNOW2, (uint8_t *) &thePacket, sizeof(thePacket)); 342 | } 343 | thePacket[j] = ESPNOW2buffer[i]; 344 | j++; 345 | } 346 | esp_now_send(ESPNOW2, (uint8_t *) &thePacket, j * sizeof(DataReading)); 347 | lenESPNOW2 = 0; 348 | break; 349 | } 350 | } 351 | } 352 | #ifdef USE_LORA 353 | void transmitLoRa(uint8_t* mac, DataReading * packet, uint8_t len) { 354 | DBG("Transmitting LoRa."); 355 | 356 | uint8_t pkt[5 + (len * sizeof(DataReading))]; 357 | memcpy(&pkt, mac, 3); 358 | memcpy(&pkt[3], &selfAddress[4], 2); 359 | memcpy(&pkt[5], packet, len * sizeof(DataReading)); 360 | LoRa.beginPacket(); 361 | LoRa.write((uint8_t*)&pkt, sizeof(pkt)); 362 | LoRa.endPacket(); 363 | } 364 | #endif 365 | 366 | void releaseLoRa(uint8_t interface) { 367 | #ifdef USE_LORA 368 | DBG("Releasing LoRa."); 369 | 370 | switch (interface) { 371 | case 0: 372 | { 373 | DataReading thePacket[lora_size]; 374 | int j = 0; 375 | for (int i = 0; i < lenLORAG; i++) { 376 | if ( j > lora_size) { 377 | j = 0; 378 | transmitLoRa(broadcast_mac, thePacket, j); 379 | } 380 | thePacket[j] = LORAGbuffer[i]; 381 | j++; 382 | } 383 | transmitLoRa(broadcast_mac, thePacket, j); 384 | lenLORAG = 0; 385 | 386 | break; 387 | } 388 | case 1: 389 | { 390 | DataReading thePacket[lora_size]; 391 | int j = 0; 392 | for (int i = 0; i < lenLORA1; i++) { 393 | if ( j > lora_size) { 394 | j = 0; 395 | transmitLoRa(LoRa1, thePacket, j); 396 | } 397 | thePacket[j] = LORA1buffer[i]; 398 | j++; 399 | } 400 | transmitLoRa(LoRa1, thePacket, j); 401 | lenLORA1 = 0; 402 | break; 403 | } 404 | case 2: 405 | { 406 | DataReading thePacket[lora_size]; 407 | int j = 0; 408 | for (int i = 0; i < lenLORA2; i++) { 409 | if ( j > lora_size) { 410 | j = 0; 411 | transmitLoRa(LoRa2, thePacket, j); 412 | } 413 | thePacket[j] = LORA2buffer[i]; 414 | j++; 415 | } 416 | transmitLoRa(LoRa2, thePacket, j); 417 | lenLORA2 = 0; 418 | 419 | break; 420 | } 421 | } 422 | #endif 423 | } 424 | void releaseSerial() { 425 | DBG("Releasing Serial."); 426 | DynamicJsonDocument doc(24576); 427 | for (int i = 0; i < lenSERIAL; i++) { 428 | doc[i]["id"] = SERIALbuffer[i].id; 429 | doc[i]["type"] = SERIALbuffer[i].t; 430 | doc[i]["data"] = SERIALbuffer[i].d; 431 | } 432 | serializeJson(doc, UART_IF); 433 | UART_IF.println(); 434 | lenSERIAL = 0; 435 | } 436 | void releaseMQTT() { 437 | #ifdef USE_WIFI 438 | DBG("Releasing MQTT."); 439 | DynamicJsonDocument doc(24576); 440 | for (int i = 0; i < lenMQTT; i++) { 441 | doc[i]["id"] = MQTTbuffer[i].id; 442 | doc[i]["type"] = MQTTbuffer[i].t; 443 | doc[i]["data"] = MQTTbuffer[i].d; 444 | } 445 | String outgoingString; 446 | serializeJson(doc, outgoingString); 447 | client.publish("esp/fdrs", (char*) outgoingString.c_str()); 448 | lenMQTT = 0; 449 | #endif 450 | } 451 | void reconnect() { 452 | #ifdef USE_WIFI 453 | // Loop until reconnected 454 | while (!client.connected()) { 455 | // Attempt to connect 456 | if (client.connect("FDRS_GATEWAY")) { 457 | // Subscribe 458 | client.subscribe("esp/fdrs"); 459 | } else { 460 | DBG("Connecting MQTT."); 461 | delay(5000); 462 | } 463 | } 464 | #endif 465 | } 466 | void begin_espnow() { 467 | DBG("Initializing ESP-NOW!"); 468 | 469 | WiFi.mode(WIFI_STA); 470 | WiFi.disconnect(); 471 | // Init ESP-NOW for either ESP8266 or ESP32 and set MAC address 472 | #if defined(ESP8266) 473 | wifi_set_macaddr(STATION_IF, selfAddress); 474 | if (esp_now_init() != 0) { 475 | return; 476 | } 477 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 478 | esp_now_register_send_cb(OnDataSent); 479 | esp_now_register_recv_cb(OnDataRecv); 480 | // Register peers 481 | esp_now_add_peer(ESPNOW1, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 482 | esp_now_add_peer(ESPNOW2, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 483 | #elif defined(ESP32) 484 | esp_wifi_set_mac(WIFI_IF_STA, &selfAddress[0]); 485 | if (esp_now_init() != ESP_OK) { 486 | DBG("Error initializing ESP-NOW"); 487 | return; 488 | } 489 | esp_now_register_send_cb(OnDataSent); 490 | esp_now_register_recv_cb(OnDataRecv); 491 | 492 | peerInfo.channel = 0; 493 | peerInfo.encrypt = false; 494 | // Register first peer 495 | 496 | memcpy(peerInfo.peer_addr, broadcast_mac, 6); 497 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 498 | DBG("Failed to add peer bcast"); 499 | return; 500 | } 501 | memcpy(peerInfo.peer_addr, ESPNOW1, 6); 502 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 503 | DBG("Failed to add peer 1"); 504 | return; 505 | } 506 | memcpy(peerInfo.peer_addr, ESPNOW2, 6); 507 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 508 | DBG("Failed to add peer 2"); 509 | return; 510 | } 511 | 512 | #endif 513 | DBG(" ESP-NOW Initialized."); 514 | } 515 | -------------------------------------------------------------------------------- /Examples/4_UART_Gateway/4_UART_Gateway.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // GATEWAY 2.000 4 | // 5 | // Developed by Timm Bogner (timmbogner@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // 7 | 8 | #define DEBUG 9 | #define CREDENTIALS 10 | 11 | #include "fdrs_config.h" 12 | 13 | #ifdef ESP8266 14 | #include 15 | #include 16 | #elif defined(ESP32) 17 | #include 18 | #include 19 | #include 20 | #endif 21 | #include 22 | #ifdef USE_WIFI 23 | #include 24 | #endif 25 | #ifdef USE_LORA 26 | #include 27 | #endif 28 | #ifdef USE_LED 29 | #include 30 | #endif 31 | #include "fdrs_functions.h" 32 | 33 | void setup() { 34 | #if defined(ESP8266) 35 | Serial.begin(115200); 36 | #elif defined(ESP32) 37 | Serial.begin(115200); 38 | UART_IF.begin(115200, SERIAL_8N1, RXD2, TXD2); 39 | #endif 40 | DBG("Address:" + String (UNIT_MAC, HEX)); 41 | #ifdef USE_LED 42 | FastLED.addLeds(leds, NUM_LEDS); 43 | leds[0] = CRGB::Blue; 44 | FastLED.show(); 45 | #endif 46 | #ifdef USE_WIFI 47 | delay(10); 48 | WiFi.begin(ssid, password); 49 | while (WiFi.status() != WL_CONNECTED) { 50 | DBG("Connecting to WiFi..."); 51 | delay(500); 52 | } 53 | DBG("WiFi Connected"); 54 | client.setServer(mqtt_server, 1883); 55 | if (!client.connected()) { 56 | DBG("Connecting MQTT..."); 57 | reconnect(); 58 | } 59 | DBG("MQTT Connected"); 60 | client.setCallback(mqtt_callback); 61 | #else 62 | begin_espnow(); 63 | #endif 64 | #ifdef USE_LORA 65 | DBG("Initializing LoRa!"); 66 | SPI.begin(SCK, MISO, MOSI, SS); 67 | LoRa.setPins(SS, RST, DIO0); 68 | if (!LoRa.begin(GLOBAL_BAND)) { 69 | while (1); 70 | } 71 | DBG(" LoRa initialized."); 72 | #endif 73 | 74 | //DBG(sizeof(DataReading)); 75 | 76 | } 77 | 78 | void loop() { 79 | if (millis() > timeESPNOWG) { 80 | timeESPNOWG += ESPNOWG_DELAY; 81 | if (lenESPNOWG > 0) releaseESPNOW(0); 82 | } 83 | if (millis() > timeESPNOW1) { 84 | timeESPNOW1 += ESPNOW1_DELAY; 85 | if (lenESPNOW1 > 0) releaseESPNOW(1); 86 | } 87 | if (millis() > timeESPNOW2) { 88 | timeESPNOW2 += ESPNOW2_DELAY; 89 | if (lenESPNOW2 > 0) releaseESPNOW(2); 90 | } 91 | if (millis() > timeSERIAL) { 92 | timeSERIAL += SERIAL_DELAY; 93 | if (lenSERIAL > 0) releaseSerial(); 94 | } 95 | if (millis() > timeMQTT) { 96 | timeMQTT += MQTT_DELAY; 97 | if (lenMQTT > 0) releaseMQTT(); 98 | } 99 | if (millis() > timeLORAG) { 100 | timeLORAG += LORAG_DELAY; 101 | if (lenLORAG > 0) releaseLoRa(0); 102 | } 103 | if (millis() > timeLORA1) { 104 | timeLORA1 += LORA1_DELAY; 105 | if (lenLORA1 > 0) releaseLoRa(1); 106 | } 107 | if (millis() > timeLORA2) { 108 | timeLORA2 += LORA2_DELAY; 109 | if (lenLORA2 > 0) releaseLoRa(2); 110 | } 111 | 112 | while (UART_IF.available()) { 113 | getSerial(); 114 | } 115 | getLoRa(); 116 | #ifdef USE_WIFI 117 | if (!client.connected()) { 118 | DBG("Connecting MQTT..."); 119 | reconnect(); 120 | } 121 | client.loop(); 122 | #endif 123 | if (newData) { 124 | switch (newData) { 125 | case 1: //ESP-NOW #1 126 | ESPNOW1_ACT 127 | break; 128 | case 2: //ESP-NOW #2 129 | ESPNOW2_ACT 130 | break; 131 | case 3: //ESP-NOW General 132 | ESPNOWG_ACT 133 | break; 134 | case 4: //Serial 135 | SERIAL_ACT 136 | break; 137 | case 5: //MQTT 138 | MQTT_ACT 139 | break; 140 | case 6: //LoRa General 141 | LORAG_ACT 142 | break; 143 | case 7: //LoRa #1 144 | LORA1_ACT 145 | break; 146 | case 8: //LoRa #2 147 | LORA2_ACT 148 | break; 149 | } 150 | newData = 0; 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /Examples/4_UART_Gateway/fdrs_config.h: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // GATEWAY 2.000 Configuration 4 | 5 | #include //Uncomment if you install the globals file 6 | #define DEBUG 7 | 8 | #define UNIT_MAC 0x04 // The address of this gateway 9 | 10 | //Actions -- Define what happens when a packet arrives at each interface: 11 | //Current function options are: sendESPNOW(MAC), sendSerial(), sendMQTT(), bufferESPNOW(interface), bufferSerial(), and bufferLoRa(interface). 12 | 13 | #define ESPNOWG_ACT sendSerial(); 14 | #define SERIAL_ACT //sendMQTT(); 15 | #define MQTT_ACT 16 | #define LORAG_ACT sendSerial(); 17 | 18 | #define USE_LORA 19 | //#define USE_WIFI //Used only for MQTT gateway 20 | 21 | #define WIFI_SSID "Your SSID" 22 | #define WIFI_PASS "Your Password" 23 | #define MQTT_ADDR "192.168.0.8" 24 | 25 | // Peer addresses 26 | #define ESPNOW1_PEER 0x04 // ESPNOW1 Address 27 | #define ESPNOW2_PEER 0x05 // ESPNOW2 Address 28 | #define LORA1_PEER 0x04 // LoRa1 Address 29 | #define LORA2_PEER 0x05 // LoRa2 Address 30 | 31 | // Peer Actions 32 | #define ESPNOW1_ACT 33 | #define ESPNOW2_ACT 34 | #define LORA1_ACT 35 | #define LORA2_ACT 36 | 37 | #if defined (ESP32) 38 | #define RXD2 14 39 | #define TXD2 15 40 | #define UART_IF Serial2 41 | #else 42 | #define UART_IF Serial 43 | #endif 44 | 45 | //LoRa Configuration -- Needed only if using LoRa 46 | #define SCK 5 47 | #define MISO 19 48 | #define MOSI 27 49 | #define SS 18 50 | #define RST 14 51 | #define DIO0 26 52 | //433E6 for Asia 53 | //866E6 for Europe 54 | //915E6 for North America 55 | //#define BAND 915E6 56 | //#define SF 7 57 | 58 | // Buffer Delays - in milliseconds 59 | #define ESPNOW1_DELAY 0 60 | #define ESPNOW2_DELAY 0 61 | #define ESPNOWG_DELAY 0 62 | #define SERIAL_DELAY 0 63 | #define MQTT_DELAY 0 64 | #define LORAG_DELAY 1000 65 | #define LORA1_DELAY 1000 66 | #define LORA2_DELAY 1000 67 | //#define USE_LED //Not yet fully implemented 68 | #define LED_PIN 32 69 | #define NUM_LEDS 4 70 | 71 | // MQTT Topics 72 | #define TOPIC_DATA "FDRS/DATA" 73 | #define TOPIC_STATUS "FDRS/STATUS" 74 | #define TOPIC_COMMAND "FDRS/COMMAND" 75 | -------------------------------------------------------------------------------- /Examples/4_UART_Gateway/fdrs_functions.h: -------------------------------------------------------------------------------- 1 | #ifdef DEBUG 2 | #define DBG(a) (Serial.println(a)) 3 | #else 4 | #define DBG(a) 5 | #endif 6 | 7 | typedef struct __attribute__((packed)) DataReading { 8 | float d; 9 | uint16_t id; 10 | uint8_t t; 11 | 12 | } DataReading; 13 | 14 | const uint8_t espnow_size = 250 / sizeof(DataReading); 15 | const uint8_t lora_size = 256 / sizeof(DataReading); 16 | const uint8_t mac_prefix[] = {MAC_PREFIX}; 17 | #if defined(ESP32) 18 | esp_now_peer_info_t peerInfo; 19 | #endif 20 | 21 | uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 22 | uint8_t selfAddress[] = {MAC_PREFIX, UNIT_MAC}; 23 | uint8_t ESPNOW1[] = {MAC_PREFIX, ESPNOW1_PEER}; 24 | uint8_t ESPNOW2[] = {MAC_PREFIX, ESPNOW2_PEER}; 25 | uint8_t incMAC[6]; 26 | uint8_t LoRa1[] = {mac_prefix[3], mac_prefix[4], LORA1_PEER}; 27 | uint8_t LoRa2[] = {mac_prefix[3], mac_prefix[4], LORA2_PEER}; 28 | 29 | DataReading theData[256]; 30 | uint8_t ln; 31 | uint8_t newData = 0; 32 | 33 | DataReading ESPNOW1buffer[256]; 34 | uint8_t lenESPNOW1 = 0; 35 | uint32_t timeESPNOW1 = 0; 36 | DataReading ESPNOW2buffer[256]; 37 | uint8_t lenESPNOW2 = 0; 38 | uint32_t timeESPNOW2 = 0; 39 | DataReading ESPNOWGbuffer[256]; 40 | uint8_t lenESPNOWG = 0; 41 | uint32_t timeESPNOWG = 0; 42 | DataReading SERIALbuffer[256]; 43 | uint8_t lenSERIAL = 0; 44 | uint32_t timeSERIAL = 0; 45 | DataReading MQTTbuffer[256]; 46 | uint8_t lenMQTT = 0; 47 | uint32_t timeMQTT = 0; 48 | DataReading LORAGbuffer[256]; 49 | uint8_t lenLORAG = 0; 50 | uint32_t timeLORAG = 0; 51 | DataReading LORA1buffer[256]; 52 | uint8_t lenLORA1 = 0; 53 | uint32_t timeLORA1 = 0; 54 | DataReading LORA2buffer[256]; 55 | uint8_t lenLORA2 = 0; 56 | uint32_t timeLORA2 = 0; 57 | 58 | WiFiClient espClient; 59 | #ifdef USE_LED 60 | CRGB leds[NUM_LEDS]; 61 | #endif 62 | #ifdef USE_WIFI 63 | PubSubClient client(espClient); 64 | const char* ssid = WIFI_NET; 65 | const char* password = WIFI_PASS; 66 | const char* mqtt_server = MQTT_ADDR; 67 | #endif 68 | 69 | 70 | // Set ESP-NOW send and receive callbacks for either ESP8266 or ESP32 71 | #if defined(ESP8266) 72 | void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { 73 | } 74 | void OnDataRecv(uint8_t* mac, uint8_t *incomingData, uint8_t len) { 75 | #elif defined(ESP32) 76 | void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { 77 | } 78 | void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { 79 | #endif 80 | memcpy(&theData, incomingData, sizeof(theData)); 81 | memcpy(&incMAC, mac, sizeof(incMAC)); 82 | if (memcmp(&incMAC, &ESPNOW1, 6) == 0) newData = 1; 83 | else if (memcmp(&incMAC, &ESPNOW2, 6) == 0) newData = 2; 84 | else newData = 3; 85 | ln = len / sizeof(DataReading); 86 | DBG("Incoming ESP-NOW."); 87 | } 88 | void getSerial() { 89 | String incomingString = UART_IF.readStringUntil('\n'); 90 | DynamicJsonDocument doc(24576); 91 | DeserializationError error = deserializeJson(doc, incomingString); 92 | if (error) { // Test if parsing succeeds. 93 | // DBG("json parse err"); 94 | // DBG(incomingString); 95 | return; 96 | } else { 97 | int s = doc.size(); 98 | //UART_IF.println(s); 99 | for (int i = 0; i < s; i++) { 100 | theData[i].id = doc[i]["id"]; 101 | theData[i].t = doc[i]["type"]; 102 | theData[i].d = doc[i]["data"]; 103 | } 104 | ln = s; 105 | newData = 4; 106 | DBG("Incoming Serial."); 107 | 108 | } 109 | } 110 | void mqtt_callback(char* topic, byte * message, unsigned int length) { 111 | String incomingString; 112 | for (int i = 0; i < length; i++) { 113 | incomingString += (char)message[i]; 114 | } 115 | StaticJsonDocument<2048> doc; 116 | DeserializationError error = deserializeJson(doc, incomingString); 117 | if (error) { // Test if parsing succeeds. 118 | DBG("json parse err"); 119 | DBG(incomingString); 120 | return; 121 | } else { 122 | int s = doc.size(); 123 | //UART_IF.println(s); 124 | for (int i = 0; i < s; i++) { 125 | theData[i].id = doc[i]["id"]; 126 | theData[i].t = doc[i]["type"]; 127 | theData[i].d = doc[i]["data"]; 128 | } 129 | ln = s; 130 | newData = 5; 131 | DBG("Incoming MQTT."); 132 | 133 | } 134 | } 135 | 136 | void getLoRa() { 137 | #ifdef USE_LORA 138 | int packetSize = LoRa.parsePacket(); 139 | if (packetSize) { 140 | uint8_t packet[packetSize]; 141 | uint8_t incLORAMAC[2]; 142 | LoRa.readBytes((uint8_t *)&packet, packetSize); 143 | /*for (int i = 0; i < packetSize; i++) { 144 | Serial.println(packet[i], HEX); 145 | } 146 | */ 147 | if (memcmp(&packet, &selfAddress[3], 3) == 0) { //Check if addressed to this device 148 | DBG("Packet for me"); 149 | memcpy(&incLORAMAC, &packet[3], 2); //Split off address portion of packet 150 | memcpy(&theData, &packet[5], packetSize - 5); //Split off data portion of packet 151 | if (memcmp(&incLORAMAC, &LoRa1, 2) == 0) { 152 | newData = 7; //Check if it is from a registered sender 153 | DBG("From Registred sender"); 154 | } 155 | else if (memcmp(&incLORAMAC, &LoRa2, 2) == 0) newData = 8; 156 | else newData = 6; 157 | DBG (newData); 158 | ln = (packetSize - 5) / sizeof(DataReading); 159 | newData = 6; 160 | DBG("Incoming LoRa."); 161 | 162 | 163 | } 164 | } 165 | #endif 166 | } 167 | 168 | void sendESPNOW(uint8_t address) { 169 | DBG("Sending ESP-NOW."); 170 | uint8_t NEWPEER[] = {MAC_PREFIX, address}; 171 | #if defined(ESP32) 172 | esp_now_peer_info_t peerInfo; 173 | peerInfo.ifidx = WIFI_IF_STA; 174 | peerInfo.channel = 0; 175 | peerInfo.encrypt = false; 176 | memcpy(peerInfo.peer_addr, NEWPEER, 6); 177 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 178 | DBG("Failed to add peer"); 179 | return; 180 | } 181 | #endif 182 | 183 | DataReading thePacket[ln]; 184 | int j = 0; 185 | for (int i = 0; i < ln; i++) { 186 | if ( j > espnow_size) { 187 | j = 0; 188 | esp_now_send(NEWPEER, (uint8_t *) &thePacket, sizeof(thePacket)); 189 | } 190 | thePacket[j] = theData[i]; 191 | j++; 192 | } 193 | esp_now_send(NEWPEER, (uint8_t *) &thePacket, j * sizeof(DataReading)); 194 | esp_now_del_peer(NEWPEER); 195 | } 196 | 197 | void sendSerial() { 198 | DBG("Sending Serial."); 199 | DynamicJsonDocument doc(24576); 200 | for (int i = 0; i < ln; i++) { 201 | doc[i]["id"] = theData[i].id; 202 | doc[i]["type"] = theData[i].t; 203 | doc[i]["data"] = theData[i].d; 204 | } 205 | serializeJson(doc, UART_IF); 206 | UART_IF.println(); 207 | 208 | #ifndef ESP8266 209 | serializeJson(doc, Serial); 210 | Serial.println(); 211 | #endif 212 | 213 | } 214 | void sendMQTT() { 215 | #ifdef USE_WIFI 216 | DBG("Sending MQTT."); 217 | DynamicJsonDocument doc(24576); 218 | for (int i = 0; i < ln; i++) { 219 | doc[i]["id"] = theData[i].id; 220 | doc[i]["type"] = theData[i].t; 221 | doc[i]["data"] = theData[i].d; 222 | } 223 | String outgoingString; 224 | serializeJson(doc, outgoingString); 225 | client.publish("esp/fdrs", (char*) outgoingString.c_str()); 226 | #endif 227 | } 228 | 229 | void bufferESPNOW(uint8_t interface) { 230 | DBG("Buffering ESP-NOW."); 231 | 232 | switch (interface) { 233 | case 0: 234 | for (int i = 0; i < ln; i++) { 235 | ESPNOWGbuffer[lenESPNOWG + i] = theData[i]; 236 | } 237 | lenESPNOWG += ln; 238 | break; 239 | case 1: 240 | for (int i = 0; i < ln; i++) { 241 | ESPNOW1buffer[lenESPNOW1 + i] = theData[i]; 242 | } 243 | lenESPNOW1 += ln; 244 | break; 245 | case 2: 246 | for (int i = 0; i < ln; i++) { 247 | ESPNOW2buffer[lenESPNOW2 + i] = theData[i]; 248 | } 249 | lenESPNOW2 += ln; 250 | break; 251 | } 252 | } 253 | void bufferSerial() { 254 | DBG("Buffering Serial."); 255 | for (int i = 0; i < ln; i++) { 256 | SERIALbuffer[lenSERIAL + i] = theData[i]; 257 | } 258 | lenSERIAL += ln; 259 | //UART_IF.println("SENDSERIAL:" + String(lenSERIAL) + " "); 260 | } 261 | void bufferMQTT() { 262 | DBG("Buffering MQTT."); 263 | for (int i = 0; i < ln; i++) { 264 | MQTTbuffer[lenMQTT + i] = theData[i]; 265 | } 266 | lenMQTT += ln; 267 | } 268 | //void bufferLoRa() { 269 | // for (int i = 0; i < ln; i++) { 270 | // LORAbuffer[lenLORA + i] = theData[i]; 271 | // } 272 | // lenLORA += ln; 273 | //} 274 | void bufferLoRa(uint8_t interface) { 275 | DBG("Buffering LoRa."); 276 | switch (interface) { 277 | case 0: 278 | for (int i = 0; i < ln; i++) { 279 | LORAGbuffer[lenLORAG + i] = theData[i]; 280 | } 281 | lenLORAG += ln; 282 | break; 283 | case 1: 284 | for (int i = 0; i < ln; i++) { 285 | LORA1buffer[lenLORA1 + i] = theData[i]; 286 | } 287 | lenLORA1 += ln; 288 | break; 289 | case 2: 290 | for (int i = 0; i < ln; i++) { 291 | LORA2buffer[lenLORA2 + i] = theData[i]; 292 | } 293 | lenLORA2 += ln; 294 | break; 295 | } 296 | } 297 | 298 | void releaseESPNOW(uint8_t interface) { 299 | DBG("Releasing ESP-NOW."); 300 | switch (interface) { 301 | case 0: 302 | { 303 | DataReading thePacket[espnow_size]; 304 | int j = 0; 305 | for (int i = 0; i < lenESPNOWG; i++) { 306 | if ( j > espnow_size) { 307 | j = 0; 308 | esp_now_send(broadcast_mac, (uint8_t *) &thePacket, sizeof(thePacket)); 309 | } 310 | thePacket[j] = ESPNOWGbuffer[i]; 311 | j++; 312 | } 313 | esp_now_send(broadcast_mac, (uint8_t *) &thePacket, j * sizeof(DataReading)); 314 | lenESPNOWG = 0; 315 | break; 316 | } 317 | case 1: 318 | { 319 | DataReading thePacket[espnow_size]; 320 | int j = 0; 321 | for (int i = 0; i < lenESPNOW1; i++) { 322 | if ( j > espnow_size) { 323 | j = 0; 324 | esp_now_send(ESPNOW1, (uint8_t *) &thePacket, sizeof(thePacket)); 325 | } 326 | thePacket[j] = ESPNOW1buffer[i]; 327 | j++; 328 | } 329 | esp_now_send(ESPNOW1, (uint8_t *) &thePacket, j * sizeof(DataReading)); 330 | lenESPNOW1 = 0; 331 | break; 332 | } 333 | case 2: 334 | { 335 | DataReading thePacket[espnow_size]; 336 | int j = 0; 337 | for (int i = 0; i < lenESPNOW2; i++) { 338 | if ( j > espnow_size) { 339 | j = 0; 340 | esp_now_send(ESPNOW2, (uint8_t *) &thePacket, sizeof(thePacket)); 341 | } 342 | thePacket[j] = ESPNOW2buffer[i]; 343 | j++; 344 | } 345 | esp_now_send(ESPNOW2, (uint8_t *) &thePacket, j * sizeof(DataReading)); 346 | lenESPNOW2 = 0; 347 | break; 348 | } 349 | } 350 | } 351 | #ifdef USE_LORA 352 | void transmitLoRa(uint8_t* mac, DataReading * packet, uint8_t len) { 353 | DBG("Transmitting LoRa."); 354 | 355 | uint8_t pkt[5 + (len * sizeof(DataReading))]; 356 | memcpy(&pkt, mac, 3); 357 | memcpy(&pkt[3], &selfAddress[4], 2); 358 | memcpy(&pkt[5], packet, len * sizeof(DataReading)); 359 | LoRa.beginPacket(); 360 | LoRa.write((uint8_t*)&pkt, sizeof(pkt)); 361 | LoRa.endPacket(); 362 | } 363 | #endif 364 | 365 | void releaseLoRa(uint8_t interface) { 366 | #ifdef USE_LORA 367 | DBG("Releasing LoRa."); 368 | 369 | switch (interface) { 370 | case 0: 371 | { 372 | DataReading thePacket[lora_size]; 373 | int j = 0; 374 | for (int i = 0; i < lenLORAG; i++) { 375 | if ( j > lora_size) { 376 | j = 0; 377 | transmitLoRa(broadcast_mac, thePacket, j); 378 | } 379 | thePacket[j] = LORAGbuffer[i]; 380 | j++; 381 | } 382 | transmitLoRa(broadcast_mac, thePacket, j); 383 | lenLORAG = 0; 384 | 385 | break; 386 | } 387 | case 1: 388 | { 389 | DataReading thePacket[lora_size]; 390 | int j = 0; 391 | for (int i = 0; i < lenLORA1; i++) { 392 | if ( j > lora_size) { 393 | j = 0; 394 | transmitLoRa(LoRa1, thePacket, j); 395 | } 396 | thePacket[j] = LORA1buffer[i]; 397 | j++; 398 | } 399 | transmitLoRa(LoRa1, thePacket, j); 400 | lenLORA1 = 0; 401 | break; 402 | } 403 | case 2: 404 | { 405 | DataReading thePacket[lora_size]; 406 | int j = 0; 407 | for (int i = 0; i < lenLORA2; i++) { 408 | if ( j > lora_size) { 409 | j = 0; 410 | transmitLoRa(LoRa2, thePacket, j); 411 | } 412 | thePacket[j] = LORA2buffer[i]; 413 | j++; 414 | } 415 | transmitLoRa(LoRa2, thePacket, j); 416 | lenLORA2 = 0; 417 | 418 | break; 419 | } 420 | } 421 | #endif 422 | } 423 | void releaseSerial() { 424 | DBG("Releasing Serial."); 425 | DynamicJsonDocument doc(24576); 426 | for (int i = 0; i < lenSERIAL; i++) { 427 | doc[i]["id"] = SERIALbuffer[i].id; 428 | doc[i]["type"] = SERIALbuffer[i].t; 429 | doc[i]["data"] = SERIALbuffer[i].d; 430 | } 431 | serializeJson(doc, UART_IF); 432 | UART_IF.println(); 433 | lenSERIAL = 0; 434 | } 435 | void releaseMQTT() { 436 | #ifdef USE_WIFI 437 | DBG("Releasing MQTT."); 438 | DynamicJsonDocument doc(24576); 439 | for (int i = 0; i < lenMQTT; i++) { 440 | doc[i]["id"] = MQTTbuffer[i].id; 441 | doc[i]["type"] = MQTTbuffer[i].t; 442 | doc[i]["data"] = MQTTbuffer[i].d; 443 | } 444 | String outgoingString; 445 | serializeJson(doc, outgoingString); 446 | client.publish("esp/fdrs", (char*) outgoingString.c_str()); 447 | lenMQTT = 0; 448 | #endif 449 | } 450 | void reconnect() { 451 | #ifdef USE_WIFI 452 | // Loop until reconnected 453 | while (!client.connected()) { 454 | // Attempt to connect 455 | if (client.connect("FDRS_GATEWAY")) { 456 | // Subscribe 457 | client.subscribe("esp/fdrs"); 458 | } else { 459 | DBG("Connecting MQTT."); 460 | delay(5000); 461 | } 462 | } 463 | #endif 464 | } 465 | void begin_espnow() { 466 | DBG("Initializing ESP-NOW!"); 467 | 468 | WiFi.mode(WIFI_STA); 469 | WiFi.disconnect(); 470 | // Init ESP-NOW for either ESP8266 or ESP32 and set MAC address 471 | #if defined(ESP8266) 472 | wifi_set_macaddr(STATION_IF, selfAddress); 473 | if (esp_now_init() != 0) { 474 | return; 475 | } 476 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 477 | esp_now_register_send_cb(OnDataSent); 478 | esp_now_register_recv_cb(OnDataRecv); 479 | // Register peers 480 | esp_now_add_peer(ESPNOW1, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 481 | esp_now_add_peer(ESPNOW2, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 482 | #elif defined(ESP32) 483 | esp_wifi_set_mac(WIFI_IF_STA, &selfAddress[0]); 484 | if (esp_now_init() != ESP_OK) { 485 | DBG("Error initializing ESP-NOW"); 486 | return; 487 | } 488 | esp_now_register_send_cb(OnDataSent); 489 | esp_now_register_recv_cb(OnDataRecv); 490 | 491 | peerInfo.channel = 0; 492 | peerInfo.encrypt = false; 493 | // Register first peer 494 | memcpy(peerInfo.peer_addr, broadcast_mac, 6); 495 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 496 | DBG("Failed to add peer bcast"); 497 | return; 498 | } 499 | memcpy(peerInfo.peer_addr, ESPNOW1, 6); 500 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 501 | DBG("Failed to add peer 1"); 502 | return; 503 | } 504 | memcpy(peerInfo.peer_addr, ESPNOW2, 6); 505 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 506 | DBG("Failed to add peer 2"); 507 | return; 508 | } 509 | #endif 510 | DBG(" ESP-NOW Initialized."); 511 | } 512 | -------------------------------------------------------------------------------- /Examples/5_MQTT_Gateway/5_MQTT_Gateway.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // GATEWAY 2.000 4 | // 5 | // Developed by Timm Bogner (timmbogner@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // 7 | 8 | #include "fdrs_config.h" 9 | #ifdef ESP8266 10 | #include 11 | #include 12 | #elif defined(ESP32) 13 | #include 14 | #include 15 | #include 16 | #endif 17 | #include 18 | #ifdef USE_WIFI 19 | #include 20 | #endif 21 | #ifdef USE_LORA 22 | #include 23 | #endif 24 | #ifdef USE_LED 25 | #include 26 | #endif 27 | #include "fdrs_functions.h" 28 | 29 | void setup() { 30 | #if defined(ESP8266) 31 | Serial.begin(115200); 32 | #elif defined(ESP32) 33 | Serial.begin(115200); 34 | UART_IF.begin(115200, SERIAL_8N1, RXD2, TXD2); 35 | #endif 36 | DBG("Address:" + String (UNIT_MAC, HEX)); 37 | #ifdef USE_LED 38 | FastLED.addLeds(leds, NUM_LEDS); 39 | leds[0] = CRGB::Blue; 40 | FastLED.show(); 41 | #endif 42 | #ifdef USE_WIFI 43 | delay(10); 44 | WiFi.begin(ssid, password); 45 | while (WiFi.status() != WL_CONNECTED) { 46 | DBG("Connecting to WiFi..."); 47 | DBG(FDRS_WIFI_SSID); 48 | 49 | delay(500); 50 | } 51 | DBG("WiFi Connected"); 52 | client.setServer(mqtt_server, 1883); 53 | if (!client.connected()) { 54 | DBG("Connecting MQTT..."); 55 | reconnect(); 56 | } 57 | DBG("MQTT Connected"); 58 | client.setCallback(mqtt_callback); 59 | #else 60 | begin_espnow(); 61 | #endif 62 | #ifdef USE_LORA 63 | DBG("Initializing LoRa!"); 64 | SPI.begin(SCK, MISO, MOSI, SS); 65 | LoRa.setPins(SS, RST, DIO0); 66 | if (!LoRa.begin(FDRS_BAND)) { 67 | while (1); 68 | } 69 | LoRa.setSpreadingFactor(FDRS_SF); 70 | DBG(" LoRa initialized."); 71 | #endif 72 | 73 | //DBG(sizeof(DataReading)); 74 | #ifdef USE_WIFI 75 | client.publish(TOPIC_STATUS, "FDRS initialized"); 76 | #endif 77 | } 78 | 79 | void loop() { 80 | #ifdef ESPNOWG_DELAY 81 | if (millis() > timeESPNOWG) { 82 | timeESPNOWG += ESPNOWG_DELAY; 83 | if (lenESPNOWG > 0) releaseESPNOW(0); 84 | } 85 | #endif 86 | #ifdef ESPNOW1_DELAY 87 | if (millis() > timeESPNOW1) { 88 | timeESPNOW1 += ESPNOW1_DELAY; 89 | if (lenESPNOW1 > 0) releaseESPNOW(1); 90 | } 91 | #endif 92 | #ifdef ESPNOW2_DELAY 93 | if (millis() > timeESPNOW2) { 94 | timeESPNOW2 += ESPNOW2_DELAY; 95 | if (lenESPNOW2 > 0) releaseESPNOW(2); 96 | } 97 | #endif 98 | #ifdef SERIAL_DELAY 99 | if (millis() > timeSERIAL) { 100 | timeSERIAL += SERIAL_DELAY; 101 | if (lenSERIAL > 0) releaseSerial(); 102 | } 103 | #endif 104 | #ifdef MQTT_DELAY 105 | if (millis() > timeMQTT) { 106 | timeMQTT += MQTT_DELAY; 107 | if (lenMQTT > 0) releaseMQTT(); 108 | } 109 | #endif 110 | #ifdef LORAG_DELAY 111 | if (millis() > timeLORAG) { 112 | timeLORAG += LORAG_DELAY; 113 | if (lenLORAG > 0) releaseLoRa(0); 114 | } 115 | #endif 116 | #ifdef LORA1_DELAY 117 | if (millis() > timeLORA1) { 118 | timeLORA1 += LORA1_DELAY; 119 | if (lenLORA1 > 0) releaseLoRa(1); 120 | } 121 | #endif 122 | #ifdef LORA2_DELAY 123 | if (millis() > timeLORA2) { 124 | timeLORA2 += LORA2_DELAY; 125 | if (lenLORA2 > 0) releaseLoRa(2); 126 | } 127 | #endif 128 | 129 | while (UART_IF.available()) { 130 | getSerial(); 131 | } 132 | getLoRa(); 133 | #ifdef USE_WIFI 134 | if (!client.connected()) { 135 | DBG("Connecting MQTT..."); 136 | reconnect(); 137 | } 138 | client.loop(); 139 | #endif 140 | if (newData) { 141 | switch (newData) { 142 | case 1: //ESP-NOW #1 143 | ESPNOW1_ACT 144 | break; 145 | case 2: //ESP-NOW #2 146 | ESPNOW2_ACT 147 | break; 148 | case 3: //ESP-NOW General 149 | ESPNOWG_ACT 150 | break; 151 | case 4: //Serial 152 | SERIAL_ACT 153 | break; 154 | case 5: //MQTT 155 | MQTT_ACT 156 | break; 157 | case 6: //LoRa General 158 | LORAG_ACT 159 | break; 160 | case 7: //LoRa #1 161 | LORA1_ACT 162 | break; 163 | case 8: //LoRa #2 164 | LORA2_ACT 165 | break; 166 | } 167 | newData = 0; 168 | } 169 | } 170 | -------------------------------------------------------------------------------- /Examples/5_MQTT_Gateway/fdrs_config.h: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // GATEWAY 2.000 Configuration 4 | 5 | #include //Uncomment if you install the globals file 6 | #define DEBUG 7 | 8 | #define UNIT_MAC 0x00 // The address of this gateway 9 | 10 | //Actions -- Define what happens when a packet arrives at each interface: 11 | //Current function options are: sendESPNOW(MAC), sendSerial(), sendMQTT(), bufferESPNOW(interface), bufferSerial(), and bufferLoRa(interface). 12 | 13 | #define ESPNOWG_ACT 14 | #define SERIAL_ACT sendMQTT(); 15 | #define MQTT_ACT 16 | #define LORAG_ACT 17 | 18 | //#define USE_LORA 19 | #define USE_WIFI //Used only for MQTT gateway 20 | 21 | #define WIFI_SSID "Your SSID" 22 | #define WIFI_PASS "Your Password" 23 | #define MQTT_ADDR "192.168.0.8" 24 | 25 | // Peer addresses 26 | #define ESPNOW1_PEER 0x04 // ESPNOW1 Address 27 | #define ESPNOW2_PEER 0x05 // ESPNOW2 Address 28 | #define LORA1_PEER 0x04 // LoRa1 Address 29 | #define LORA2_PEER 0x05 // LoRa2 Address 30 | 31 | // Peer Actions 32 | #define ESPNOW1_ACT 33 | #define ESPNOW2_ACT 34 | #define LORA1_ACT 35 | #define LORA2_ACT 36 | 37 | //Pins for UART data interface (ESP32 only) 38 | #define RXD2 14 39 | #define TXD2 15 40 | 41 | //LoRa Configuration -- Needed only if using LoRa 42 | #define SCK 5 43 | #define MISO 19 44 | #define MOSI 27 45 | #define SS 18 46 | #define RST 14 47 | #define DIO0 26 48 | //433E6 for Asia 49 | //866E6 for Europe 50 | //915E6 for North America 51 | #define BAND 915E6 52 | #define SF 7 53 | 54 | // Buffer Delays - in milliseconds 55 | //#define ESPNOW1_DELAY 0 56 | //#define ESPNOW2_DELAY 0 57 | //#define ESPNOWG_DELAY 0 58 | //#define SERIAL_DELAY 0 59 | //#define MQTT_DELAY 0 60 | //#define LORAG_DELAY 1000 61 | //#define LORA1_DELAY 1000 62 | //#define LORA2_DELAY 1000 63 | //#define USE_LED //Not yet fully implemented 64 | #define LED_PIN 32 65 | #define NUM_LEDS 4 66 | 67 | // MQTT Topics 68 | #define TOPIC_DATA "FDRS/DATA" 69 | #define TOPIC_STATUS "FDRS/STATUS" 70 | #define TOPIC_COMMAND "FDRS/COMMAND" 71 | -------------------------------------------------------------------------------- /Examples/5_MQTT_Gateway/fdrs_functions.h: -------------------------------------------------------------------------------- 1 | #ifdef DEBUG 2 | #define DBG(a) (Serial.println(a)) 3 | #else 4 | #define DBG(a) 5 | #endif 6 | 7 | #if defined (ESP32) 8 | #define UART_IF Serial1 9 | #else 10 | #define UART_IF Serial 11 | #endif 12 | 13 | #ifdef GLOBALS 14 | #define FDRS_WIFI_SSID GLOBAL_SSID 15 | #define FDRS_WIFI_PASS GLOBAL_PASS 16 | #define FDRS_MQTT_ADDR GLOBAL_MQTT_ADDR 17 | #define FDRS_BAND GLOBAL_BAND 18 | #define FDRS_SF GLOBAL_SF 19 | #else 20 | #define FDRS_WIFI_SSID WIFI_SSID 21 | #define FDRS_WIFI_PASS WIFI_PASS 22 | #define FDRS_MQTT_ADDR MQTT_ADDR 23 | #define FDRS_BAND BAND 24 | #define FDRS_SF SF 25 | #endif 26 | 27 | #define MAC_PREFIX 0xAA, 0xBB, 0xCC, 0xDD, 0xEE // Should only be changed if implementing multiple FDRS systems. 28 | 29 | typedef struct __attribute__((packed)) DataReading { 30 | float d; 31 | uint16_t id; 32 | uint8_t t; 33 | 34 | } DataReading; 35 | 36 | const uint8_t espnow_size = 250 / sizeof(DataReading); 37 | const uint8_t lora_size = 256 / sizeof(DataReading); 38 | const uint8_t mac_prefix[] = {MAC_PREFIX}; 39 | 40 | #ifdef ESP32 41 | esp_now_peer_info_t peerInfo; 42 | #endif 43 | 44 | uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 45 | uint8_t selfAddress[] = {MAC_PREFIX, UNIT_MAC}; 46 | uint8_t incMAC[6]; 47 | 48 | #ifdef ESPNOW1_PEER 49 | uint8_t ESPNOW1[] = {MAC_PREFIX, ESPNOW1_PEER}; 50 | #else 51 | uint8_t ESPNOW1[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 52 | #endif 53 | #ifdef ESPNOW2_PEER 54 | uint8_t ESPNOW2[] = {MAC_PREFIX, ESPNOW2_PEER}; 55 | #else 56 | uint8_t ESPNOW2[] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; 57 | #endif 58 | 59 | #ifdef USE_LORA 60 | uint8_t LoRa1[] = {mac_prefix[3], mac_prefix[4], LORA1_PEER}; 61 | uint8_t LoRa2[] = {mac_prefix[3], mac_prefix[4], LORA2_PEER}; 62 | //uint8_t LoRaAddress[] = {0x42, 0x00}; 63 | #endif 64 | 65 | DataReading theData[256]; 66 | uint8_t ln; 67 | uint8_t newData = 0; 68 | 69 | DataReading ESPNOW1buffer[256]; 70 | uint8_t lenESPNOW1 = 0; 71 | uint32_t timeESPNOW1 = 0; 72 | DataReading ESPNOW2buffer[256]; 73 | uint8_t lenESPNOW2 = 0; 74 | uint32_t timeESPNOW2 = 0; 75 | DataReading ESPNOWGbuffer[256]; 76 | uint8_t lenESPNOWG = 0; 77 | uint32_t timeESPNOWG = 0; 78 | DataReading SERIALbuffer[256]; 79 | uint8_t lenSERIAL = 0; 80 | uint32_t timeSERIAL = 0; 81 | DataReading MQTTbuffer[256]; 82 | uint8_t lenMQTT = 0; 83 | uint32_t timeMQTT = 0; 84 | DataReading LORAGbuffer[256]; 85 | uint8_t lenLORAG = 0; 86 | uint32_t timeLORAG = 0; 87 | DataReading LORA1buffer[256]; 88 | uint8_t lenLORA1 = 0; 89 | uint32_t timeLORA1 = 0; 90 | DataReading LORA2buffer[256]; 91 | uint8_t lenLORA2 = 0; 92 | uint32_t timeLORA2 = 0; 93 | 94 | WiFiClient espClient; 95 | #ifdef USE_LED 96 | CRGB leds[NUM_LEDS]; 97 | #endif 98 | #ifdef USE_WIFI 99 | PubSubClient client(espClient); 100 | const char* ssid = FDRS_WIFI_SSID; 101 | const char* password = FDRS_WIFI_PASS; 102 | const char* mqtt_server = FDRS_MQTT_ADDR; 103 | #endif 104 | 105 | 106 | // Set ESP-NOW send and receive callbacks for either ESP8266 or ESP32 107 | #if defined(ESP8266) 108 | void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { 109 | } 110 | void OnDataRecv(uint8_t* mac, uint8_t *incomingData, uint8_t len) { 111 | #elif defined(ESP32) 112 | void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { 113 | } 114 | void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { 115 | #endif 116 | memcpy(&theData, incomingData, sizeof(theData)); 117 | memcpy(&incMAC, mac, sizeof(incMAC)); 118 | if (memcmp(&incMAC, &ESPNOW1, 6) == 0) newData = 1; 119 | else if (memcmp(&incMAC, &ESPNOW2, 6) == 0) newData = 2; 120 | else newData = 3; 121 | ln = len / sizeof(DataReading); 122 | DBG("Incoming ESP-NOW."); 123 | } 124 | void getSerial() { 125 | String incomingString = UART_IF.readStringUntil('\n'); 126 | DynamicJsonDocument doc(24576); 127 | DeserializationError error = deserializeJson(doc, incomingString); 128 | if (error) { // Test if parsing succeeds. 129 | // DBG("json parse err"); 130 | // DBG(incomingString); 131 | return; 132 | } else { 133 | int s = doc.size(); 134 | //UART_IF.println(s); 135 | for (int i = 0; i < s; i++) { 136 | theData[i].id = doc[i]["id"]; 137 | theData[i].t = doc[i]["type"]; 138 | theData[i].d = doc[i]["data"]; 139 | } 140 | ln = s; 141 | newData = 4; 142 | DBG("Incoming Serial."); 143 | 144 | } 145 | } 146 | void mqtt_callback(char* topic, byte * message, unsigned int length) { 147 | String incomingString; 148 | DBG(topic); 149 | for (int i = 0; i < length; i++) { 150 | incomingString += (char)message[i]; 151 | } 152 | StaticJsonDocument<2048> doc; 153 | DeserializationError error = deserializeJson(doc, incomingString); 154 | if (error) { // Test if parsing succeeds. 155 | DBG("json parse err"); 156 | DBG(incomingString); 157 | return; 158 | } else { 159 | int s = doc.size(); 160 | //UART_IF.println(s); 161 | for (int i = 0; i < s; i++) { 162 | theData[i].id = doc[i]["id"]; 163 | theData[i].t = doc[i]["type"]; 164 | theData[i].d = doc[i]["data"]; 165 | } 166 | ln = s; 167 | newData = 5; 168 | DBG("Incoming MQTT."); 169 | 170 | } 171 | } 172 | 173 | void getLoRa() { 174 | #ifdef USE_LORA 175 | int packetSize = LoRa.parsePacket(); 176 | if (packetSize) { 177 | uint8_t packet[packetSize]; 178 | uint8_t incLORAMAC[2]; 179 | LoRa.readBytes((uint8_t *)&packet, packetSize); 180 | // for (int i = 0; i < packetSize; i++) { 181 | // UART_IF.println(packet[i], HEX); 182 | // } 183 | if (memcmp(&packet, &selfAddress[3], 3) == 0) { //Check if addressed to this device 184 | memcpy(&incLORAMAC, &packet[3], 2); //Split off address portion of packet 185 | memcpy(&theData, &packet[5], packetSize - 5); //Split off data portion of packet 186 | if (memcmp(&incLORAMAC, &LoRa1, 2) == 0) newData = 7; //Check if it is from a registered sender 187 | else if (memcmp(&incLORAMAC, &LoRa2, 2) == 0) newData = 8; 188 | else newData = 6; 189 | ln = (packetSize - 5) / sizeof(DataReading); 190 | newData = 6; 191 | DBG("Incoming LoRa."); 192 | 193 | } 194 | } 195 | #endif 196 | } 197 | 198 | void sendESPNOW(uint8_t address) { 199 | DBG("Sending ESP-NOW."); 200 | uint8_t NEWPEER[] = {MAC_PREFIX, address}; 201 | #if defined(ESP32) 202 | esp_now_peer_info_t peerInfo; 203 | peerInfo.ifidx = WIFI_IF_STA; 204 | peerInfo.channel = 0; 205 | peerInfo.encrypt = false; 206 | memcpy(peerInfo.peer_addr, NEWPEER, 6); 207 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 208 | DBG("Failed to add peer"); 209 | return; 210 | } 211 | #endif 212 | 213 | DataReading thePacket[ln]; 214 | int j = 0; 215 | for (int i = 0; i < ln; i++) { 216 | if ( j > espnow_size) { 217 | j = 0; 218 | esp_now_send(NEWPEER, (uint8_t *) &thePacket, sizeof(thePacket)); 219 | } 220 | thePacket[j] = theData[i]; 221 | j++; 222 | } 223 | esp_now_send(NEWPEER, (uint8_t *) &thePacket, j * sizeof(DataReading)); 224 | esp_now_del_peer(NEWPEER); 225 | } 226 | 227 | void sendSerial() { 228 | DBG("Sending Serial."); 229 | DynamicJsonDocument doc(24576); 230 | for (int i = 0; i < ln; i++) { 231 | doc[i]["id"] = theData[i].id; 232 | doc[i]["type"] = theData[i].t; 233 | doc[i]["data"] = theData[i].d; 234 | } 235 | serializeJson(doc, UART_IF); 236 | UART_IF.println(); 237 | 238 | #ifndef ESP8266 239 | serializeJson(doc, Serial); 240 | Serial.println(); 241 | #endif 242 | 243 | } 244 | void sendMQTT() { 245 | #ifdef USE_WIFI 246 | DBG("Sending MQTT."); 247 | DynamicJsonDocument doc(24576); 248 | for (int i = 0; i < ln; i++) { 249 | doc[i]["id"] = theData[i].id; 250 | doc[i]["type"] = theData[i].t; 251 | doc[i]["data"] = theData[i].d; 252 | } 253 | String outgoingString; 254 | serializeJson(doc, outgoingString); 255 | client.publish(TOPIC_DATA, (char*) outgoingString.c_str()); 256 | #endif 257 | } 258 | 259 | void bufferESPNOW(uint8_t interface) { 260 | DBG("Buffering ESP-NOW."); 261 | 262 | switch (interface) { 263 | case 0: 264 | for (int i = 0; i < ln; i++) { 265 | ESPNOWGbuffer[lenESPNOWG + i] = theData[i]; 266 | } 267 | lenESPNOWG += ln; 268 | break; 269 | case 1: 270 | for (int i = 0; i < ln; i++) { 271 | ESPNOW1buffer[lenESPNOW1 + i] = theData[i]; 272 | } 273 | lenESPNOW1 += ln; 274 | break; 275 | case 2: 276 | for (int i = 0; i < ln; i++) { 277 | ESPNOW2buffer[lenESPNOW2 + i] = theData[i]; 278 | } 279 | lenESPNOW2 += ln; 280 | break; 281 | } 282 | } 283 | void bufferSerial() { 284 | DBG("Buffering Serial."); 285 | for (int i = 0; i < ln; i++) { 286 | SERIALbuffer[lenSERIAL + i] = theData[i]; 287 | } 288 | lenSERIAL += ln; 289 | //UART_IF.println("SENDSERIAL:" + String(lenSERIAL) + " "); 290 | } 291 | void bufferMQTT() { 292 | DBG("Buffering MQTT."); 293 | for (int i = 0; i < ln; i++) { 294 | MQTTbuffer[lenMQTT + i] = theData[i]; 295 | } 296 | lenMQTT += ln; 297 | } 298 | //void bufferLoRa() { 299 | // for (int i = 0; i < ln; i++) { 300 | // LORAbuffer[lenLORA + i] = theData[i]; 301 | // } 302 | // lenLORA += ln; 303 | //} 304 | void bufferLoRa(uint8_t interface) { 305 | DBG("Buffering LoRa."); 306 | switch (interface) { 307 | case 0: 308 | for (int i = 0; i < ln; i++) { 309 | LORAGbuffer[lenLORAG + i] = theData[i]; 310 | } 311 | lenLORAG += ln; 312 | break; 313 | case 1: 314 | for (int i = 0; i < ln; i++) { 315 | LORA1buffer[lenLORA1 + i] = theData[i]; 316 | } 317 | lenLORA1 += ln; 318 | break; 319 | case 2: 320 | for (int i = 0; i < ln; i++) { 321 | LORA2buffer[lenLORA2 + i] = theData[i]; 322 | } 323 | lenLORA2 += ln; 324 | break; 325 | } 326 | } 327 | 328 | void releaseESPNOW(uint8_t interface) { 329 | DBG("Releasing ESP-NOW."); 330 | switch (interface) { 331 | case 0: 332 | { 333 | DataReading thePacket[espnow_size]; 334 | int j = 0; 335 | for (int i = 0; i < lenESPNOWG; i++) { 336 | if ( j > espnow_size) { 337 | j = 0; 338 | esp_now_send(broadcast_mac, (uint8_t *) &thePacket, sizeof(thePacket)); 339 | } 340 | thePacket[j] = ESPNOWGbuffer[i]; 341 | j++; 342 | } 343 | esp_now_send(broadcast_mac, (uint8_t *) &thePacket, j * sizeof(DataReading)); 344 | lenESPNOWG = 0; 345 | break; 346 | } 347 | case 1: 348 | { 349 | DataReading thePacket[espnow_size]; 350 | int j = 0; 351 | for (int i = 0; i < lenESPNOW1; i++) { 352 | if ( j > espnow_size) { 353 | j = 0; 354 | esp_now_send(ESPNOW1, (uint8_t *) &thePacket, sizeof(thePacket)); 355 | } 356 | thePacket[j] = ESPNOW1buffer[i]; 357 | j++; 358 | } 359 | esp_now_send(ESPNOW1, (uint8_t *) &thePacket, j * sizeof(DataReading)); 360 | lenESPNOW1 = 0; 361 | break; 362 | } 363 | case 2: 364 | { 365 | DataReading thePacket[espnow_size]; 366 | int j = 0; 367 | for (int i = 0; i < lenESPNOW2; i++) { 368 | if ( j > espnow_size) { 369 | j = 0; 370 | esp_now_send(ESPNOW2, (uint8_t *) &thePacket, sizeof(thePacket)); 371 | } 372 | thePacket[j] = ESPNOW2buffer[i]; 373 | j++; 374 | } 375 | esp_now_send(ESPNOW2, (uint8_t *) &thePacket, j * sizeof(DataReading)); 376 | lenESPNOW2 = 0; 377 | break; 378 | } 379 | } 380 | } 381 | #ifdef USE_LORA 382 | void transmitLoRa(uint8_t* mac, DataReading * packet, uint8_t len) { 383 | DBG("Transmitting LoRa."); 384 | 385 | uint8_t pkt[5 + (len * sizeof(DataReading))]; 386 | memcpy(&pkt, mac, 3); 387 | memcpy(&pkt[3], &selfAddress[4], 2); 388 | memcpy(&pkt[5], packet, len * sizeof(DataReading)); 389 | LoRa.beginPacket(); 390 | LoRa.write((uint8_t*)&pkt, sizeof(pkt)); 391 | LoRa.endPacket(); 392 | } 393 | #endif 394 | 395 | void releaseLoRa(uint8_t interface) { 396 | #ifdef USE_LORA 397 | DBG("Releasing LoRa."); 398 | 399 | switch (interface) { 400 | case 0: 401 | { 402 | DataReading thePacket[lora_size]; 403 | int j = 0; 404 | for (int i = 0; i < lenLORAG; i++) { 405 | if ( j > lora_size) { 406 | j = 0; 407 | transmitLoRa(broadcast_mac, thePacket, j); 408 | } 409 | thePacket[j] = LORAGbuffer[i]; 410 | j++; 411 | } 412 | transmitLoRa(broadcast_mac, thePacket, j); 413 | lenLORAG = 0; 414 | 415 | break; 416 | } 417 | case 1: 418 | { 419 | DataReading thePacket[lora_size]; 420 | int j = 0; 421 | for (int i = 0; i < lenLORA1; i++) { 422 | if ( j > lora_size) { 423 | j = 0; 424 | transmitLoRa(LoRa1, thePacket, j); 425 | } 426 | thePacket[j] = LORA1buffer[i]; 427 | j++; 428 | } 429 | transmitLoRa(LoRa1, thePacket, j); 430 | lenLORA1 = 0; 431 | break; 432 | } 433 | case 2: 434 | { 435 | DataReading thePacket[lora_size]; 436 | int j = 0; 437 | for (int i = 0; i < lenLORA2; i++) { 438 | if ( j > lora_size) { 439 | j = 0; 440 | transmitLoRa(LoRa2, thePacket, j); 441 | } 442 | thePacket[j] = LORA2buffer[i]; 443 | j++; 444 | } 445 | transmitLoRa(LoRa2, thePacket, j); 446 | lenLORA2 = 0; 447 | 448 | break; 449 | } 450 | } 451 | #endif 452 | } 453 | void releaseSerial() { 454 | DBG("Releasing Serial."); 455 | DynamicJsonDocument doc(24576); 456 | for (int i = 0; i < lenSERIAL; i++) { 457 | doc[i]["id"] = SERIALbuffer[i].id; 458 | doc[i]["type"] = SERIALbuffer[i].t; 459 | doc[i]["data"] = SERIALbuffer[i].d; 460 | } 461 | serializeJson(doc, UART_IF); 462 | UART_IF.println(); 463 | lenSERIAL = 0; 464 | } 465 | void releaseMQTT() { 466 | #ifdef USE_WIFI 467 | DBG("Releasing MQTT."); 468 | DynamicJsonDocument doc(24576); 469 | for (int i = 0; i < lenMQTT; i++) { 470 | doc[i]["id"] = MQTTbuffer[i].id; 471 | doc[i]["type"] = MQTTbuffer[i].t; 472 | doc[i]["data"] = MQTTbuffer[i].d; 473 | } 474 | String outgoingString; 475 | serializeJson(doc, outgoingString); 476 | client.publish(TOPIC_DATA, (char*) outgoingString.c_str()); 477 | lenMQTT = 0; 478 | #endif 479 | } 480 | void reconnect() { 481 | #ifdef USE_WIFI 482 | // Loop until reconnected 483 | while (!client.connected()) { 484 | // Attempt to connect 485 | if (client.connect("FDRS_GATEWAY")) { 486 | // Subscribe 487 | client.subscribe(TOPIC_COMMAND); 488 | } else { 489 | DBG("Connecting MQTT."); 490 | delay(5000); 491 | } 492 | } 493 | #endif 494 | } 495 | void begin_espnow() { 496 | DBG("Initializing ESP-NOW!"); 497 | WiFi.mode(WIFI_STA); 498 | WiFi.disconnect(); 499 | // Init ESP-NOW for either ESP8266 or ESP32 and set MAC address 500 | #if defined(ESP8266) 501 | wifi_set_macaddr(STATION_IF, selfAddress); 502 | if (esp_now_init() != 0) { 503 | return; 504 | } 505 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 506 | esp_now_register_send_cb(OnDataSent); 507 | esp_now_register_recv_cb(OnDataRecv); 508 | // Register peers 509 | #ifdef ESPNOW1_PEER 510 | esp_now_add_peer(ESPNOW1, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 511 | #endif 512 | #ifdef ESPNOW2_PEER 513 | esp_now_add_peer(ESPNOW2, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 514 | #endif 515 | #elif defined(ESP32) 516 | esp_wifi_set_mac(WIFI_IF_STA, &selfAddress[0]); 517 | if (esp_now_init() != ESP_OK) { 518 | DBG("Error initializing ESP-NOW"); 519 | return; 520 | } 521 | esp_now_register_send_cb(OnDataSent); 522 | esp_now_register_recv_cb(OnDataRecv); 523 | 524 | peerInfo.channel = 0; 525 | peerInfo.encrypt = false; 526 | // Register first peer 527 | 528 | memcpy(peerInfo.peer_addr, broadcast_mac, 6); 529 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 530 | DBG("Failed to add peer bcast"); 531 | return; 532 | } 533 | #ifdef ESPNOW1_PEER 534 | memcpy(peerInfo.peer_addr, ESPNOW1, 6); 535 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 536 | DBG("Failed to add peer 1"); 537 | return; 538 | } 539 | #endif 540 | #ifdef ESPNOW2_PEER 541 | memcpy(peerInfo.peer_addr, ESPNOW2, 6); 542 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 543 | DBG("Failed to add peer 2"); 544 | return; 545 | } 546 | #endif 547 | #endif 548 | DBG(" ESP-NOW Initialized."); 549 | } 550 | -------------------------------------------------------------------------------- /Examples/FDRS_Install/Instructions.txt: -------------------------------------------------------------------------------- 1 | Please copy this directory into your Arduino "libraries" directory. 2 | Edit the files in your libraries folder according to your needs with your WiFi info, LoRa band, etc. 3 | Never edit them here!!! -------------------------------------------------------------------------------- /Examples/FDRS_Install/fdrs_globals.h: -------------------------------------------------------------------------------- 1 | #define GLOBAL_SSID "Your SSID" 2 | #define GLOBAL_PASS "Password" 3 | #define GLOBAL_MQTT_ADDR "192.168.0.8" 4 | 5 | #define GLOBAL_BAND 915E6 //LoRa Frequency Band 6 | #define GLOBAL_SF 7 //LoRa Spreading Factor 7 | 8 | #define STATUS_T 0 // Status 9 | #define TEMP_T 1 // Temperature 10 | #define TEMP2_T 2 // Temperature #2 11 | #define HUMIDITY_T 3 // Relative Humidity 12 | #define PRESSURE_T 4 // Atmospheric Pressure 13 | #define LIGHT_T 5 // Light (lux) 14 | #define SOIL_T 6 // Soil Moisture 15 | #define SOIL2_T 7 // Soil Moisture #2 16 | #define SOILR_T 8 // Soil Resistance 17 | #define SOILR2_T 9 // Soil Resistance #2 18 | #define OXYGEN_T 10 // Oxygen 19 | #define CO2_T 11 // Carbon Dioxide 20 | #define WINDSPD_T 12 // Wind Speed 21 | #define WINDHDG_T 13 // Wind Direction 22 | #define RAINFALL_T 14 // Rainfall 23 | #define MOTION_T 15 // Motion 24 | #define VOLTAGE_T 16 // Voltage 25 | #define VOLTAGE2_T 17 // Voltage #2 26 | #define CURRENT_T 18 // Current 27 | #define CURRENT2_T 19 // Current #2 28 | #define IT_T 20 // Iterations 29 | #define MAC_PREFIX 0xAA, 0xBB, 0xCC, 0xDD, 0xEE // Should only be changed if implementing multiple FDRS systems. 30 | uint8_t LoRaAddress[] = {0x42, 0x00}; // do not change!!! 31 | 32 | #define GLOBALS -------------------------------------------------------------------------------- /FDRS_Gateway2000/Advanced_Setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SensorsIot/Farm-Data-Relay-System/93f5d22febcb65608d90dcdb7bb07e8ffc874080/FDRS_Gateway2000/Advanced_Setup.png -------------------------------------------------------------------------------- /FDRS_Gateway2000/Advanced_Setup_LoRa.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SensorsIot/Farm-Data-Relay-System/93f5d22febcb65608d90dcdb7bb07e8ffc874080/FDRS_Gateway2000/Advanced_Setup_LoRa.png -------------------------------------------------------------------------------- /FDRS_Gateway2000/Basic_LoRa_Setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SensorsIot/Farm-Data-Relay-System/93f5d22febcb65608d90dcdb7bb07e8ffc874080/FDRS_Gateway2000/Basic_LoRa_Setup.png -------------------------------------------------------------------------------- /FDRS_Gateway2000/Basic_Setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SensorsIot/Farm-Data-Relay-System/93f5d22febcb65608d90dcdb7bb07e8ffc874080/FDRS_Gateway2000/Basic_Setup.png -------------------------------------------------------------------------------- /FDRS_Gateway2000/DataReading.h: -------------------------------------------------------------------------------- 1 | 2 | typedef struct __attribute__((packed)) DataReading { 3 | float d; 4 | uint16_t id; 5 | uint8_t t; 6 | 7 | } DataReading; 8 | 9 | //Type definitions in progress: 10 | 11 | #define STATUS_T 0 // Status 12 | #define TEMP_T 1 // Temperature 13 | #define TEMP2_T 2 // Temperature #2 14 | #define HUMIDITY_T 3 // Relative Humidity 15 | #define PRESSURE_T 4 // Atmospheric Pressure 16 | #define LIGHT_T 5 // Light (lux) 17 | #define SOIL_T 6 // Soil Moisture 18 | #define SOIL2_T 7 // Soil Moisture #2 19 | #define OXYGEN_T 8 // Oxygen 20 | #define CO2_T 9 // Carbon Dioxide 21 | #define WINDSPD_T 10 // Wind Speed 22 | #define WINDHDG_T 11 // Wind Direction 23 | #define RAINFALL_T 12 // Rainfall 24 | #define MOTION_T 13 // Motion 25 | #define VOLTAGE_T 14 // Voltage 26 | #define VOLTAGE2_T 15 // Voltage #2 27 | #define CURRENT_T 16 // Current 28 | #define CURRENT2_T 17 // Current #2 29 | #define IT_T 18 // Iterations 30 | -------------------------------------------------------------------------------- /FDRS_Gateway2000/FDRS_Gateway2000.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // GATEWAY 2.000 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // 7 | #include "fdrs_config.h" 8 | #include "DataReading.h" 9 | #ifdef ESP8266 10 | #include 11 | #include 12 | #elif defined(ESP32) 13 | #include 14 | #include 15 | #include 16 | #endif 17 | #include 18 | #ifdef USE_WIFI 19 | #include 20 | #endif 21 | #ifdef USE_LORA 22 | #include 23 | #endif 24 | #ifdef USE_LED 25 | #include 26 | #endif 27 | #include "fdrs_functions.h" 28 | //#ifdef CREDENTIALS 29 | //#include 30 | //#endif 31 | 32 | void setup() { 33 | #if defined(ESP8266) 34 | UART_IF.begin(115200); 35 | #elif defined(ESP32) 36 | Serial.begin(115200); 37 | UART_IF.begin(115200, SERIAL_8N1, RXD2, TXD2); 38 | #endif 39 | DBG("Initializing FDRS Gateway"); 40 | DBG("Address:" + String (UNIT_MAC, HEX)); 41 | #ifdef USE_LED 42 | FastLED.addLeds(leds, NUM_LEDS); 43 | leds[0] = CRGB::Blue; 44 | FastLED.show(); 45 | #endif 46 | #ifdef USE_WIFI 47 | delay(10); 48 | WiFi.begin(ssid, password); 49 | while (WiFi.status() != WL_CONNECTED) { 50 | Serial.println("Connecting to Wifi..."); 51 | 52 | delay(500); 53 | } 54 | client.setServer(mqtt_server, 1883); 55 | //client.setServer("192.168.137.249", 1883); 56 | if (!client.connected()) { 57 | Serial.println("Connecting mqtt..."); 58 | reconnect(); 59 | } 60 | client.setCallback(mqtt_callback); 61 | #else 62 | begin_espnow(); 63 | #endif 64 | #ifdef USE_LORA 65 | DBG("Initializing LoRa!"); 66 | SPI.begin(SCK, MISO, MOSI, SS); 67 | LoRa.setPins(SS, RST, DIO0); 68 | if (!LoRa.begin(BAND)) { 69 | while (1); 70 | } 71 | DBG(" LoRa initialized."); 72 | #endif 73 | // UART_IF.println(sizeof(DataReading)); 74 | 75 | } 76 | 77 | void loop() { 78 | if (millis() > timeESPNOWG) { 79 | timeESPNOWG += ESPNOWG_DELAY; 80 | if (lenESPNOWG > 0) releaseESPNOW(0); 81 | } 82 | if (millis() > timeESPNOW1) { 83 | timeESPNOW1 += ESPNOW1_DELAY; 84 | if (lenESPNOW1 > 0) releaseESPNOW(1); 85 | } 86 | if (millis() > timeESPNOW2) { 87 | timeESPNOW2 += ESPNOW2_DELAY; 88 | if (lenESPNOW2 > 0) releaseESPNOW(2); 89 | } 90 | if (millis() > timeSERIAL) { 91 | timeSERIAL += SERIAL_DELAY; 92 | if (lenSERIAL > 0) releaseSerial(); 93 | } 94 | if (millis() > timeMQTT) { 95 | timeMQTT += MQTT_DELAY; 96 | if (lenMQTT > 0) releaseMQTT(); 97 | } 98 | if (millis() > timeLORAG) { 99 | timeLORAG += LORAG_DELAY; 100 | if (lenLORAG > 0) releaseLoRa(0); 101 | } 102 | if (millis() > timeLORA1) { 103 | timeLORA1 += LORA1_DELAY; 104 | if (lenLORA1 > 0) releaseLoRa(1); 105 | } 106 | if (millis() > timeLORA2) { 107 | timeLORA2 += LORA2_DELAY; 108 | if (lenLORA2 > 0) releaseLoRa(2); 109 | } 110 | 111 | while (UART_IF.available()) { 112 | getSerial(); 113 | } 114 | getLoRa(); 115 | #ifdef USE_WIFI 116 | if (!client.connected()) { 117 | Serial.println("Connecting mqtt..."); 118 | reconnect(); 119 | } 120 | client.loop(); 121 | #endif 122 | if (newData) { 123 | switch (newData) { 124 | case 1: //ESP-NOW #1 125 | ESPNOW1_ACT 126 | break; 127 | case 2: //ESP-NOW #2 128 | ESPNOW2_ACT 129 | break; 130 | case 3: //ESP-NOW General 131 | ESPNOWG_ACT 132 | break; 133 | case 4: //Serial 134 | SERIAL_ACT 135 | break; 136 | case 5: //MQTT 137 | MQTT_ACT 138 | break; 139 | case 6: //LoRa General 140 | LORAG_ACT 141 | break; 142 | case 7: //LoRa #1 143 | LORA1_ACT 144 | break; 145 | case 8: //LoRa #2 146 | LORA2_ACT 147 | break; 148 | } 149 | newData = 0; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /FDRS_Gateway2000/README.md: -------------------------------------------------------------------------------- 1 | # Gateway 2.000 2 | 3 | This is the FDRS Multiprotocol Gateway sketch. The device listens for packets over ESP-NOW, UART, LoRa, and/or MQTT, then retransmits the packets over these interfaces using rules defined in the "Actions" section of the configuration file. 4 | 5 | The most commonly used configuration tells the device to take any ESP-NOW packet it receives and output the data over the serial port (UART): 6 | ``` 7 | #define UNIT_MAC 0x00 8 | #define ESPNOWG_ACT sendSerial(); 9 | ``` 10 | The companion for this device, connected via serial, takes any data it receives from the serial port and sends it via MQTT: 11 | ``` 12 | #define USE_WIFI 13 | #define SERIAL_ACT sendMQTT(); 14 | ``` 15 | Splitting the gateway into two devices allows you to use ESP-NOW and WiFi simultaneously without channel conflicts. You can also connect the first device to a computer with a USB-UART adapter and get the data that way, eliminating WiFi altogether. 16 | 17 | If you have sensors that are out of range of your first gateway, you can use a gateway as a repeater. First set the UNIT_MAC to 0x01, then send general ESP-NOW traffic to the address of the first gateway: 18 | ``` 19 | #define UNIT_MAC 0x01 20 | #define ESPNOWG_ACT sendESPNOW(0x00); 21 | ``` 22 | ### LoRa 23 | You can also use LoRa to expand the distances between hops. While ESP-NOW is quick enough to handle a lot of traffic in real-time, LoRa is much slower. For this reason, you must send LoRa data to a buffer and transmit it at standard intervals. 24 | 25 | 26 | 27 | ![Basic](/FDRS_Gateway2000/Basic_Setup.png) 28 | 29 | ![Advanced](/FDRS_Gateway2000/Advanced_Setup.png) 30 | 31 | ![Basic LoRa](/FDRS_Gateway2000/Basic_LoRa_Setup.png) 32 | 33 | ![Advanced LoRa](/FDRS_Gateway2000/Advanced_Setup_LoRa.png) 34 | 35 | -------------------------------------------------------------------------------- /FDRS_Gateway2000/defaults.h: -------------------------------------------------------------------------------- 1 | #define UNIT_MAC 0xFC // THIS UNIT 2 | #define ESPNOW1_PEER 0xFD // ESPNOW1 Address 3 | #define ESPNOW2_PEER 0xFE // ESPNOW2 Address 4 | #define LORA1_PEER 0xFD // LoRa1 Address 5 | #define LORA2_PEER 0xFE // LoRa2 Address 6 | 7 | #define ESPNOW1_DELAY 0 8 | #define ESPNOW2_DELAY 0 9 | #define ESPNOWG_DELAY 0 10 | #define SERIAL_DELAY 0 11 | #define MQTT_DELAY 0 12 | #define LORAG_DELAY 1000 13 | #define LORA1_DELAY 1000 14 | #define LORA2_DELAY 1000 15 | 16 | #define ESPNOW1_ACT 17 | #define ESPNOW2_ACT 18 | #define ESPNOWG_ACT 19 | #define SERIAL_ACT 20 | #define MQTT_ACT 21 | #define LORAG_ACT 22 | #define LORA1_ACT 23 | #define LORA2_ACT 24 | 25 | //#define RXD2 21 26 | //#define TXD2 22 27 | 28 | #define WIFI_NET "Your SSID" 29 | #define WIFI_PASS "Password" 30 | #define MQTT_ADDR "192.168.0.8" 31 | 32 | //#define USE_LORA 33 | #define SCK 5 34 | #define MISO 19 35 | #define MOSI 27 36 | #define SS 18 37 | #define RST 14 38 | #define DIO0 26 39 | //433E6 for Asia 40 | //866E6 for Europe 41 | //915E6 for North America 42 | #define BAND 915E6 43 | 44 | //#define USE_LED 45 | #define LED_PIN 32 46 | #define NUM_LEDS 4 47 | 48 | #define MAC_PREFIX 0xAA, 0xBB, 0xCC, 0xDD, 0xEE 49 | #define UART_IF Serial1 50 | -------------------------------------------------------------------------------- /FDRS_Gateway2000/fdrs_config.h: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // GATEWAY 2.000 Configuration 4 | 5 | #include "defaults.h" 6 | 7 | #define UNIT_MAC 0x00 // THIS UNIT 8 | 9 | //Actions -- Define what happens when a packet arrives at each interface: 10 | //Current function options are: sendESPNOW(MAC), sendSerial(), sendMQTT(), bufferESPNOW(interface), bufferSerial(), and bufferLoRa(interface). 11 | 12 | #define ESPNOWG_ACT sendSerial(); 13 | #define SERIAL_ACT 14 | #define MQTT_ACT 15 | #define LORAG_ACT sendSerial(); 16 | 17 | #define USE_LORA 18 | //#define USE_WIFI //Used only for MQTT gateway 19 | 20 | //#define CREDENTIALS 21 | 22 | //ESP32 Only -- Define UART pins and interface. 23 | #define RXD2 14 24 | #define TXD2 15 25 | #define UART_IF Serial1 26 | #define DEBUG 27 | 28 | //Wifi Configuration -- Needed only if this device is using MQTT 29 | #define WIFI_NET "Your SSID" 30 | #define WIFI_PASS "Password" 31 | #define MQTT_ADDR "192.168.0.8" 32 | 33 | ////LoRa Configuration -- Needed only if using LoRa 34 | #define SCK 5 35 | #define MISO 19 36 | #define MOSI 27 37 | #define SS 18 38 | #define RST 14 39 | #define DIO0 26 40 | //433E6 for Asia 41 | //866E6 for Europe 42 | //915E6 for North America 43 | #define BAND 915E6 44 | 45 | #ifdef CREDENTIALS 46 | #include 47 | #endif 48 | -------------------------------------------------------------------------------- /FDRS_Gateway2000/fdrs_functions.h: -------------------------------------------------------------------------------- 1 | #define DBG(a) 2 | #ifdef ESP8266 3 | #define UART_IF Serial 4 | #else 5 | #ifdef DEBUG 6 | #define DBG(a) (Serial.println(a)) 7 | #endif 8 | #endif 9 | const uint8_t espnow_size = 250 / sizeof(DataReading); 10 | const uint8_t lora_size = 256 / sizeof(DataReading); 11 | const uint8_t mac_prefix[] = {MAC_PREFIX}; 12 | #ifdef ESP32 13 | esp_now_peer_info_t peerInfo; 14 | #endif 15 | uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 16 | uint8_t selfAddress[] = {MAC_PREFIX, UNIT_MAC}; 17 | uint8_t ESPNOW1[] = {MAC_PREFIX, ESPNOW1_PEER}; 18 | uint8_t ESPNOW2[] = {MAC_PREFIX, ESPNOW2_PEER}; 19 | uint8_t incMAC[6]; 20 | uint8_t LoRa1[] = {mac_prefix[3], mac_prefix[4], LORA1_PEER}; 21 | uint8_t LoRa2[] = {mac_prefix[3], mac_prefix[4], LORA2_PEER}; 22 | 23 | DataReading theData[256]; 24 | uint8_t ln; 25 | uint8_t newData = 0; 26 | 27 | DataReading ESPNOW1buffer[256]; 28 | uint8_t lenESPNOW1 = 0; 29 | uint32_t timeESPNOW1 = 0; 30 | DataReading ESPNOW2buffer[256]; 31 | uint8_t lenESPNOW2 = 0; 32 | uint32_t timeESPNOW2 = 0; 33 | DataReading ESPNOWGbuffer[256]; 34 | uint8_t lenESPNOWG = 0; 35 | uint32_t timeESPNOWG = 0; 36 | DataReading SERIALbuffer[256]; 37 | uint8_t lenSERIAL = 0; 38 | uint32_t timeSERIAL = 0; 39 | DataReading MQTTbuffer[256]; 40 | uint8_t lenMQTT = 0; 41 | uint32_t timeMQTT = 0; 42 | DataReading LORAGbuffer[256]; 43 | uint8_t lenLORAG = 0; 44 | uint32_t timeLORAG = 0; 45 | DataReading LORA1buffer[256]; 46 | uint8_t lenLORA1 = 0; 47 | uint32_t timeLORA1 = 0; 48 | DataReading LORA2buffer[256]; 49 | uint8_t lenLORA2 = 0; 50 | uint32_t timeLORA2 = 0; 51 | 52 | WiFiClient espClient; 53 | #ifdef USE_LED 54 | CRGB leds[NUM_LEDS]; 55 | #endif 56 | #ifdef USE_WIFI 57 | PubSubClient client(espClient); 58 | const char* ssid = WIFI_NET; 59 | const char* password = WIFI_PASS; 60 | const char* mqtt_server = MQTT_ADDR; 61 | #endif 62 | 63 | 64 | // Set ESP-NOW send and receive callbacks for either ESP8266 or ESP32 65 | #if defined(ESP8266) 66 | void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { 67 | } 68 | void OnDataRecv(uint8_t* mac, uint8_t *incomingData, uint8_t len) { 69 | #elif defined(ESP32) 70 | void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { 71 | } 72 | void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { 73 | #endif 74 | memcpy(&theData, incomingData, sizeof(theData)); 75 | memcpy(&incMAC, mac, sizeof(incMAC)); 76 | if (memcmp(&incMAC, &ESPNOW1, 6) == 0) newData = 1; 77 | else if (memcmp(&incMAC, &ESPNOW2, 6) == 0) newData = 2; 78 | else newData = 3; 79 | ln = len / sizeof(DataReading); 80 | DBG("Incoming ESP-NOW."); 81 | } 82 | void getSerial() { 83 | String incomingString = UART_IF.readStringUntil('\n'); 84 | DynamicJsonDocument doc(24576); 85 | DeserializationError error = deserializeJson(doc, incomingString); 86 | if (error) { // Test if parsing succeeds. 87 | // DBG("json parse err"); 88 | // DBG(incomingString); 89 | return; 90 | } else { 91 | int s = doc.size(); 92 | //UART_IF.println(s); 93 | for (int i = 0; i < s; i++) { 94 | theData[i].id = doc[i]["id"]; 95 | theData[i].t = doc[i]["type"]; 96 | theData[i].d = doc[i]["data"]; 97 | } 98 | ln = s; 99 | newData = 4; 100 | DBG("Incoming Serial."); 101 | 102 | } 103 | } 104 | void mqtt_callback(char* topic, byte * message, unsigned int length) { 105 | String incomingString; 106 | for (int i = 0; i < length; i++) { 107 | incomingString += (char)message[i]; 108 | } 109 | StaticJsonDocument<2048> doc; 110 | DeserializationError error = deserializeJson(doc, incomingString); 111 | if (error) { // Test if parsing succeeds. 112 | DBG("json parse err"); 113 | DBG(incomingString); 114 | return; 115 | } else { 116 | int s = doc.size(); 117 | //UART_IF.println(s); 118 | for (int i = 0; i < s; i++) { 119 | theData[i].id = doc[i]["id"]; 120 | theData[i].t = doc[i]["type"]; 121 | theData[i].d = doc[i]["data"]; 122 | } 123 | ln = s; 124 | newData = 5; 125 | DBG("Incoming MQTT."); 126 | 127 | } 128 | } 129 | 130 | void getLoRa() { 131 | #ifdef USE_LORA 132 | int packetSize = LoRa.parsePacket(); 133 | if (packetSize) { 134 | uint8_t packet[packetSize]; 135 | uint8_t incLORAMAC[2]; 136 | LoRa.readBytes((uint8_t *)&packet, packetSize); 137 | // for (int i = 0; i < packetSize; i++) { 138 | // UART_IF.println(packet[i], HEX); 139 | // } 140 | if (memcmp(&packet, &selfAddress[3], 3) == 0) { //Check if addressed to this device 141 | memcpy(&incLORAMAC, &packet[3], 2); //Split off address portion of packet 142 | memcpy(&theData, &packet[5], packetSize - 5); //Split off data portion of packet 143 | if (memcmp(&incLORAMAC, &LoRa1, 2) == 0) newData = 7; //Check if it is from a registered sender 144 | else if (memcmp(&incLORAMAC, &LoRa2, 2) == 0) newData = 8; 145 | else newData = 6; 146 | ln = (packetSize - 5) / sizeof(DataReading); 147 | newData = 6; 148 | DBG("Incoming LoRa."); 149 | 150 | } 151 | } 152 | #endif 153 | } 154 | 155 | void sendESPNOW(uint8_t address) { 156 | DBG("Sending ESP-NOW."); 157 | uint8_t NEWPEER[] = {MAC_PREFIX, address}; 158 | #if defined(ESP32) 159 | esp_now_peer_info_t peerInfo; 160 | peerInfo.ifidx = WIFI_IF_STA; 161 | peerInfo.channel = 0; 162 | peerInfo.encrypt = false; 163 | memcpy(peerInfo.peer_addr, NEWPEER, 6); 164 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 165 | DBG("Failed to add peer"); 166 | return; 167 | } 168 | #endif 169 | 170 | DataReading thePacket[ln]; 171 | int j = 0; 172 | for (int i = 0; i < ln; i++) { 173 | if ( j > espnow_size) { 174 | j = 0; 175 | esp_now_send(NEWPEER, (uint8_t *) &thePacket, sizeof(thePacket)); 176 | } 177 | thePacket[j] = theData[i]; 178 | j++; 179 | } 180 | esp_now_send(NEWPEER, (uint8_t *) &thePacket, j * sizeof(DataReading)); 181 | esp_now_del_peer(NEWPEER); 182 | } 183 | 184 | void sendSerial() { 185 | DBG("Sending Serial."); 186 | DynamicJsonDocument doc(24576); 187 | for (int i = 0; i < ln; i++) { 188 | doc[i]["id"] = theData[i].id; 189 | doc[i]["type"] = theData[i].t; 190 | doc[i]["data"] = theData[i].d; 191 | } 192 | serializeJson(doc, UART_IF); 193 | UART_IF.println(); 194 | 195 | #ifndef ESP8266 196 | serializeJson(doc, Serial); 197 | Serial.println(); 198 | #endif 199 | 200 | } 201 | void sendMQTT() { 202 | #ifdef USE_WIFI 203 | DBG("Sending MQTT."); 204 | DynamicJsonDocument doc(24576); 205 | for (int i = 0; i < ln; i++) { 206 | doc[i]["id"] = theData[i].id; 207 | doc[i]["type"] = theData[i].t; 208 | doc[i]["data"] = theData[i].d; 209 | } 210 | String outgoingString; 211 | serializeJson(doc, outgoingString); 212 | client.publish("esp/fdrs", (char*) outgoingString.c_str()); 213 | #endif 214 | } 215 | 216 | void bufferESPNOW(uint8_t interface) { 217 | DBG("Buffering ESP-NOW."); 218 | 219 | switch (interface) { 220 | case 0: 221 | for (int i = 0; i < ln; i++) { 222 | ESPNOWGbuffer[lenESPNOWG + i] = theData[i]; 223 | } 224 | lenESPNOWG += ln; 225 | break; 226 | case 1: 227 | for (int i = 0; i < ln; i++) { 228 | ESPNOW1buffer[lenESPNOW1 + i] = theData[i]; 229 | } 230 | lenESPNOW1 += ln; 231 | break; 232 | case 2: 233 | for (int i = 0; i < ln; i++) { 234 | ESPNOW2buffer[lenESPNOW2 + i] = theData[i]; 235 | } 236 | lenESPNOW2 += ln; 237 | break; 238 | } 239 | } 240 | void bufferSerial() { 241 | DBG("Buffering Serial."); 242 | for (int i = 0; i < ln; i++) { 243 | SERIALbuffer[lenSERIAL + i] = theData[i]; 244 | } 245 | lenSERIAL += ln; 246 | //UART_IF.println("SENDSERIAL:" + String(lenSERIAL) + " "); 247 | } 248 | void bufferMQTT() { 249 | DBG("Buffering MQTT."); 250 | for (int i = 0; i < ln; i++) { 251 | MQTTbuffer[lenMQTT + i] = theData[i]; 252 | } 253 | lenMQTT += ln; 254 | } 255 | //void bufferLoRa() { 256 | // for (int i = 0; i < ln; i++) { 257 | // LORAbuffer[lenLORA + i] = theData[i]; 258 | // } 259 | // lenLORA += ln; 260 | //} 261 | void bufferLoRa(uint8_t interface) { 262 | DBG("Buffering LoRa."); 263 | switch (interface) { 264 | case 0: 265 | for (int i = 0; i < ln; i++) { 266 | LORAGbuffer[lenLORAG + i] = theData[i]; 267 | } 268 | lenLORAG += ln; 269 | break; 270 | case 1: 271 | for (int i = 0; i < ln; i++) { 272 | LORA1buffer[lenLORA1 + i] = theData[i]; 273 | } 274 | lenLORA1 += ln; 275 | break; 276 | case 2: 277 | for (int i = 0; i < ln; i++) { 278 | LORA2buffer[lenLORA2 + i] = theData[i]; 279 | } 280 | lenLORA2 += ln; 281 | break; 282 | } 283 | } 284 | 285 | void releaseESPNOW(uint8_t interface) { 286 | DBG("Releasing ESP-NOW."); 287 | switch (interface) { 288 | case 0: 289 | { 290 | DataReading thePacket[espnow_size]; 291 | int j = 0; 292 | for (int i = 0; i < lenESPNOWG; i++) { 293 | if ( j > espnow_size) { 294 | j = 0; 295 | esp_now_send(broadcast_mac, (uint8_t *) &thePacket, sizeof(thePacket)); 296 | } 297 | thePacket[j] = ESPNOWGbuffer[i]; 298 | j++; 299 | } 300 | esp_now_send(broadcast_mac, (uint8_t *) &thePacket, j * sizeof(DataReading)); 301 | lenESPNOWG = 0; 302 | break; 303 | } 304 | case 1: 305 | { 306 | DataReading thePacket[espnow_size]; 307 | int j = 0; 308 | for (int i = 0; i < lenESPNOW1; i++) { 309 | if ( j > espnow_size) { 310 | j = 0; 311 | esp_now_send(ESPNOW1, (uint8_t *) &thePacket, sizeof(thePacket)); 312 | } 313 | thePacket[j] = ESPNOW1buffer[i]; 314 | j++; 315 | } 316 | esp_now_send(ESPNOW1, (uint8_t *) &thePacket, j * sizeof(DataReading)); 317 | lenESPNOW1 = 0; 318 | break; 319 | } 320 | case 2: 321 | { 322 | DataReading thePacket[espnow_size]; 323 | int j = 0; 324 | for (int i = 0; i < lenESPNOW2; i++) { 325 | if ( j > espnow_size) { 326 | j = 0; 327 | esp_now_send(ESPNOW2, (uint8_t *) &thePacket, sizeof(thePacket)); 328 | } 329 | thePacket[j] = ESPNOW2buffer[i]; 330 | j++; 331 | } 332 | esp_now_send(ESPNOW2, (uint8_t *) &thePacket, j * sizeof(DataReading)); 333 | lenESPNOW2 = 0; 334 | break; 335 | } 336 | } 337 | } 338 | #ifdef USE_LORA 339 | void transmitLoRa(uint8_t* mac, DataReading * packet, uint8_t len) { 340 | DBG("Transmitting LoRa."); 341 | 342 | uint8_t pkt[5 + (len * sizeof(DataReading))]; 343 | memcpy(&pkt, mac, 3); 344 | memcpy(&pkt[3], &selfAddress[4], 2); 345 | memcpy(&pkt[5], packet, len * sizeof(DataReading)); 346 | LoRa.beginPacket(); 347 | LoRa.write((uint8_t*)&pkt, sizeof(pkt)); 348 | LoRa.endPacket(); 349 | } 350 | #endif 351 | 352 | void releaseLoRa(uint8_t interface) { 353 | #ifdef USE_LORA 354 | DBG("Releasing LoRa."); 355 | 356 | switch (interface) { 357 | case 0: 358 | { 359 | DataReading thePacket[lora_size]; 360 | int j = 0; 361 | for (int i = 0; i < lenLORAG; i++) { 362 | if ( j > lora_size) { 363 | j = 0; 364 | transmitLoRa(broadcast_mac, thePacket, j); 365 | } 366 | thePacket[j] = LORAGbuffer[i]; 367 | j++; 368 | } 369 | transmitLoRa(broadcast_mac, thePacket, j); 370 | lenLORAG = 0; 371 | 372 | break; 373 | } 374 | case 1: 375 | { 376 | DataReading thePacket[lora_size]; 377 | int j = 0; 378 | for (int i = 0; i < lenLORA1; i++) { 379 | if ( j > lora_size) { 380 | j = 0; 381 | transmitLoRa(LoRa1, thePacket, j); 382 | } 383 | thePacket[j] = LORA1buffer[i]; 384 | j++; 385 | } 386 | transmitLoRa(LoRa1, thePacket, j); 387 | lenLORA1 = 0; 388 | break; 389 | } 390 | case 2: 391 | { 392 | DataReading thePacket[lora_size]; 393 | int j = 0; 394 | for (int i = 0; i < lenLORA2; i++) { 395 | if ( j > lora_size) { 396 | j = 0; 397 | transmitLoRa(LoRa2, thePacket, j); 398 | } 399 | thePacket[j] = LORA2buffer[i]; 400 | j++; 401 | } 402 | transmitLoRa(LoRa2, thePacket, j); 403 | lenLORA2 = 0; 404 | 405 | break; 406 | } 407 | } 408 | #endif 409 | } 410 | void releaseSerial() { 411 | DBG("Releasing Serial."); 412 | DynamicJsonDocument doc(24576); 413 | for (int i = 0; i < lenSERIAL; i++) { 414 | doc[i]["id"] = SERIALbuffer[i].id; 415 | doc[i]["type"] = SERIALbuffer[i].t; 416 | doc[i]["data"] = SERIALbuffer[i].d; 417 | } 418 | serializeJson(doc, UART_IF); 419 | UART_IF.println(); 420 | lenSERIAL = 0; 421 | } 422 | void releaseMQTT() { 423 | #ifdef USE_WIFI 424 | DBG("Releasing MQTT."); 425 | DynamicJsonDocument doc(24576); 426 | for (int i = 0; i < lenMQTT; i++) { 427 | doc[i]["id"] = MQTTbuffer[i].id; 428 | doc[i]["type"] = MQTTbuffer[i].t; 429 | doc[i]["data"] = MQTTbuffer[i].d; 430 | } 431 | String outgoingString; 432 | serializeJson(doc, outgoingString); 433 | client.publish("esp/fdrs", (char*) outgoingString.c_str()); 434 | lenMQTT = 0; 435 | #endif 436 | } 437 | void reconnect() { 438 | #ifdef USE_WIFI 439 | // Loop until reconnected 440 | while (!client.connected()) { 441 | // Attempt to connect 442 | if (client.connect("FDRS_GATEWAY")) { 443 | // Subscribe 444 | client.subscribe("esp/fdrs"); 445 | } else { 446 | DBG("Connecting MQTT."); 447 | delay(5000); 448 | } 449 | } 450 | #endif 451 | } 452 | void begin_espnow() { 453 | DBG("Initializing ESP-NOW!"); 454 | 455 | WiFi.mode(WIFI_STA); 456 | WiFi.disconnect(); 457 | // Init ESP-NOW for either ESP8266 or ESP32 and set MAC address 458 | #if defined(ESP8266) 459 | wifi_set_macaddr(STATION_IF, selfAddress); 460 | if (esp_now_init() != 0) { 461 | return; 462 | } 463 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 464 | esp_now_register_send_cb(OnDataSent); 465 | esp_now_register_recv_cb(OnDataRecv); 466 | // Register peers 467 | esp_now_add_peer(ESPNOW1, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 468 | esp_now_add_peer(ESPNOW2, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 469 | #elif defined(ESP32) 470 | esp_wifi_set_mac(WIFI_IF_STA, &selfAddress[0]); 471 | if (esp_now_init() != ESP_OK) { 472 | DBG("Error initializing ESP-NOW"); 473 | return; 474 | } 475 | esp_now_register_send_cb(OnDataSent); 476 | esp_now_register_recv_cb(OnDataRecv); 477 | 478 | peerInfo.channel = 0; 479 | peerInfo.encrypt = false; 480 | // Register first peer 481 | 482 | memcpy(peerInfo.peer_addr, broadcast_mac, 6); 483 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 484 | DBG("Failed to add peer bcast"); 485 | return; 486 | } 487 | memcpy(peerInfo.peer_addr, ESPNOW1, 6); 488 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 489 | DBG("Failed to add peer 1"); 490 | return; 491 | } 492 | memcpy(peerInfo.peer_addr, ESPNOW2, 6); 493 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 494 | DBG("Failed to add peer 2"); 495 | return; 496 | } 497 | 498 | #endif 499 | DBG(" ESP-NOW Initialized."); 500 | DBG(WIFI_NET); 501 | } 502 | -------------------------------------------------------------------------------- /FDRS_Sensor2000/FDRS_Sensor2000.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // Basic Sensor Example 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // An example of how to send data using "fdrs_sensor.h". 7 | // 8 | 9 | #include "fdrs_sensor.h" 10 | 11 | float data1 = 42.069; 12 | float data2 = 21.0345; 13 | 14 | void setup() { 15 | beginFDRS(); 16 | } 17 | void loop() { 18 | loadFDRS(data1, HUMIDITY_T); 19 | loadFDRS(data2, TEMP_T); 20 | sendFDRS(); 21 | sleepFDRS(10); //Sleep time in seconds 22 | } 23 | -------------------------------------------------------------------------------- /FDRS_Sensor2000/fdrs_sensor.h: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // "fdrs_sensor.h" 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // 7 | #define READING_ID 21 //Unique ID for this sensor 8 | #define GTWY_MAC 0x00 //Address of the nearest gateway 9 | 10 | #define USE_ESPNOW 11 | //#define USE_LORA 12 | #define DEEP_SLEEP 13 | //#define POWER_CTRL 14 14 | #define DEBUG 15 | //#define CREDENTIALS 16 | #define MAC_PREFIX 0xAA, 0xBB, 0xCC, 0xDD, 0xEE 17 | 18 | //LoRa Configuration 19 | #define SCK 5 20 | #define MISO 19 21 | #define MOSI 27 22 | #define SS 18 23 | #define RST 14 24 | #define DIO0 26 25 | //433E6 for Asia 26 | //866E6 for Europe 27 | //915E6 for North America 28 | #define BAND 915E6 //May be overwritten if CREDENTIALS is set 29 | 30 | #ifdef CREDENTIALS 31 | #include 32 | #endif 33 | 34 | typedef struct __attribute__((packed)) DataReading { 35 | float d; 36 | uint16_t id; 37 | uint8_t t; 38 | 39 | } DataReading; 40 | 41 | #define STATUS_T 0 // Status 42 | #define TEMP_T 1 // Temperature 43 | #define TEMP2_T 2 // Temperature #2 44 | #define HUMIDITY_T 3 // Relative Humidity 45 | #define PRESSURE_T 4 // Atmospheric Pressure 46 | #define LIGHT_T 5 // Light (lux) 47 | #define SOIL_T 6 // Soil Moisture 48 | #define SOIL2_T 7 // Soil Moisture #2 49 | #define SOILR_T 8 // Soil Resistance 50 | #define SOILR2_T 9 // Soil Resistance #2 51 | #define OXYGEN_T 10 // Oxygen 52 | #define CO2_T 11 // Carbon Dioxide 53 | #define WINDSPD_T 12 // Wind Speed 54 | #define WINDHDG_T 13 // Wind Direction 55 | #define RAINFALL_T 14 // Rainfall 56 | #define MOTION_T 15 // Motion 57 | #define VOLTAGE_T 16 // Voltage 58 | #define VOLTAGE2_T 17 // Voltage #2 59 | #define CURRENT_T 18 // Current 60 | #define CURRENT2_T 19 // Current #2 61 | #define IT_T 20 // Iterations 62 | 63 | 64 | #if defined(ESP8266) 65 | #include 66 | #include 67 | #elif defined(ESP32) 68 | #include 69 | #include 70 | #include 71 | #endif 72 | 73 | #ifdef USE_LORA 74 | #include 75 | #endif 76 | 77 | #define DBG(a) 78 | #ifdef ESP8266 79 | #define UART_IF Serial 80 | #else 81 | #ifdef DEBUG 82 | #define DBG(a) (Serial.println(a)) 83 | #endif 84 | #endif 85 | 86 | const uint16_t espnow_size = 250 / sizeof(DataReading); 87 | uint8_t gatewayAddress[] = {MAC_PREFIX, GTWY_MAC}; 88 | uint8_t gtwyAddress[] = {gatewayAddress[3], gatewayAddress[4], GTWY_MAC}; 89 | uint8_t LoRaAddress[] = {0x42, 0x00}; 90 | 91 | 92 | uint32_t wait_time = 0; 93 | DataReading fdrsData[espnow_size]; 94 | uint8_t data_count = 0; 95 | 96 | void beginFDRS() { 97 | #ifdef DEBUG 98 | Serial.begin(115200); 99 | #endif 100 | DBG("FDRS Sensor ID " + String(READING_ID)+ " initializing..."); 101 | DBG(" Gateway: " + String (GTWY_MAC, HEX)); 102 | #ifdef POWER_CTRL 103 | DBG("Powering up the sensor array!"); 104 | pinMode(POWER_CTRL, OUTPUT); 105 | digitalWrite(POWER_CTRL, 1); 106 | #endif 107 | // Init ESP-NOW for either ESP8266 or ESP32 and set MAC address 108 | #ifdef USE_ESPNOW 109 | DBG("Initializing ESP-NOW!"); 110 | WiFi.mode(WIFI_STA); 111 | WiFi.disconnect(); 112 | #if defined(ESP8266) 113 | if (esp_now_init() != 0) { 114 | return; 115 | } 116 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 117 | // Register peers 118 | esp_now_add_peer(gatewayAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 119 | #elif defined(ESP32) 120 | if (esp_now_init() != ESP_OK) { 121 | DBG("Error initializing ESP-NOW"); 122 | return; 123 | } 124 | esp_now_peer_info_t peerInfo; 125 | peerInfo.ifidx = WIFI_IF_STA; 126 | peerInfo.channel = 0; 127 | peerInfo.encrypt = false; 128 | // Register first peer 129 | memcpy(peerInfo.peer_addr, gatewayAddress, 6); 130 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 131 | DBG("Failed to add peer"); 132 | return; 133 | } 134 | #endif 135 | DBG(" ESP-NOW Initialized."); 136 | #endif 137 | #ifdef USE_LORA 138 | DBG("Initializing LoRa!"); 139 | #ifndef __AVR__ 140 | SPI.begin(SCK, MISO, MOSI, SS); 141 | #endif 142 | LoRa.setPins(SS, RST, DIO0); 143 | if (!LoRa.begin(BAND)) { 144 | while (1); 145 | } 146 | DBG(" LoRa Initialized."); 147 | #endif 148 | } 149 | void transmitLoRa(uint8_t* mac, DataReading * packet, uint8_t len) { 150 | #ifdef USE_LORA 151 | uint8_t pkt[5 + (len * sizeof(DataReading))]; 152 | memcpy(&pkt, mac, 3); 153 | memcpy(&pkt[3], &LoRaAddress, 2); 154 | memcpy(&pkt[5], packet, len * sizeof(DataReading)); 155 | LoRa.beginPacket(); 156 | LoRa.write((uint8_t*)&pkt, sizeof(pkt)); 157 | LoRa.endPacket(); 158 | #endif 159 | } 160 | void sendFDRS() { 161 | DBG("Sending FDRS Packet!"); 162 | #ifdef USE_ESPNOW 163 | esp_now_send(gatewayAddress, (uint8_t *) &fdrsData, data_count * sizeof(DataReading)); 164 | delay(5); 165 | DBG(" ESP-NOW sent."); 166 | #endif 167 | #ifdef USE_LORA 168 | transmitLoRa(gtwyAddress, fdrsData, data_count); 169 | DBG(" LoRa sent."); 170 | #endif 171 | data_count = 0; 172 | } 173 | void loadFDRS(float d, uint8_t t) { 174 | DBG("Data loaded. Type: " + String(t)); 175 | if (data_count > espnow_size) sendFDRS(); 176 | DataReading dr; 177 | dr.id = READING_ID; 178 | dr.t = t; 179 | dr.d = d; 180 | fdrsData[data_count] = dr; 181 | data_count++; 182 | } 183 | void sleepFDRS(int sleep_time) { 184 | DBG("Sleepytime!"); 185 | #ifdef DEEP_SLEEP 186 | DBG(" Deep sleeping."); 187 | #ifdef ESP32 188 | esp_sleep_enable_timer_wakeup(sleep_time * 1000000); 189 | esp_deep_sleep_start(); 190 | #endif 191 | #ifdef ESP8266 192 | ESP.deepSleep(sleep_time * 1000000); 193 | #endif 194 | #endif 195 | DBG(" Delaying."); 196 | delay(sleep_time * 1000); 197 | } 198 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 timmbogner 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Original_FDRS/FDRS_Gateway/DataReading.h: -------------------------------------------------------------------------------- 1 | 2 | typedef struct DataReading { 3 | float d; 4 | uint16_t id; 5 | uint8_t t; 6 | 7 | } DataReading; 8 | -------------------------------------------------------------------------------- /Original_FDRS/FDRS_Gateway/FDRS_Gateway.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // GATEWAY MODULE 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // Setup instructions located in the "fdrs_config.h" file. 7 | 8 | #if defined(ESP8266) 9 | #include 10 | #include 11 | #elif defined(ESP32) 12 | #define RXD2 21 13 | #define TXD2 22 14 | #include 15 | #include 16 | #include 17 | #endif 18 | #include "fdrs_config.h" 19 | #include 20 | #include "DataReading.h" 21 | 22 | uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 23 | uint8_t prevAddress[] = {MAC_PREFIX, PREV_MAC}; 24 | uint8_t selfAddress[] = {MAC_PREFIX, UNIT_MAC}; 25 | DataReading incData[31]; 26 | bool newData = false; 27 | int pkt_readings; 28 | uint8_t incMAC[6]; 29 | 30 | #if defined(ESP8266) 31 | void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { 32 | } 33 | void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) { 34 | #elif defined(ESP32) 35 | void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { 36 | } 37 | void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { 38 | #endif 39 | 40 | memcpy(&incData, incomingData, len); 41 | memcpy(&incMAC, mac, 6); 42 | pkt_readings = len / sizeof(DataReading); 43 | newData = true; 44 | } 45 | 46 | void setup() { 47 | WiFi.mode(WIFI_STA); 48 | WiFi.disconnect(); 49 | // Init ESP-NOW for either ESP8266 or ESP32 and set MAC address 50 | #if defined(ESP8266) 51 | Serial.begin(115200); 52 | wifi_set_macaddr(STATION_IF, selfAddress); 53 | if (esp_now_init() != 0) { 54 | return; 55 | } 56 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 57 | esp_now_register_send_cb(OnDataSent); 58 | esp_now_register_recv_cb(OnDataRecv); 59 | // Register peer 60 | esp_now_add_peer(prevAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 61 | esp_now_add_peer(broadcast_mac, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 62 | 63 | #elif defined(ESP32) 64 | Serial.begin(115200, SERIAL_8N1, RXD2, TXD2); 65 | esp_wifi_set_mac(WIFI_IF_STA, &selfAddress[0]); 66 | if (esp_now_init() != ESP_OK) { 67 | Serial.println("Error initializing ESP-NOW"); 68 | return; 69 | } 70 | esp_now_register_send_cb(OnDataSent); 71 | esp_now_register_recv_cb(OnDataRecv); 72 | esp_now_peer_info_t peerInfo; 73 | peerInfo.channel = 0; 74 | peerInfo.encrypt = false; 75 | memcpy(peerInfo.peer_addr, prevAddress, 6); 76 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 77 | Serial.println("Failed to add peer"); 78 | return; 79 | } 80 | memcpy(peerInfo.peer_addr, broadcast_mac, 6); 81 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 82 | Serial.println("Failed to add peer"); 83 | return; 84 | } 85 | #endif 86 | } 87 | 88 | //CMD example {"id":72,"type":201,"data":166} 89 | 90 | void getSerial() { 91 | DataReading theCommands[31]; 92 | String incomingString = Serial.readStringUntil('\n'); 93 | StaticJsonDocument<2048> doc; 94 | DeserializationError error = deserializeJson(doc, incomingString); 95 | if (error) { // Test if parsing succeeds. 96 | Serial.println("parse err"); 97 | 98 | return; 99 | } else { 100 | int s = doc.size(); 101 | //Serial.println(s); 102 | for (int i = 0; i < s; i++) { 103 | if (i > 31) break; 104 | theCommands[i].id = doc[i]["id"]; 105 | theCommands[i].t = doc[i]["type"]; 106 | theCommands[i].d = doc[i]["data"]; 107 | } 108 | esp_now_send(broadcast_mac, (uint8_t *) &theCommands, s * sizeof(DataReading)); 109 | 110 | } 111 | } 112 | 113 | void encodeJSON() { 114 | StaticJsonDocument<2048> doc; 115 | for (int i = 0; i < pkt_readings; i++) { 116 | doc[i]["id"] = incData[i].id; 117 | doc[i]["type"] = incData[i].t; 118 | doc[i]["data"] = incData[i].d; 119 | incData[i].id = 0; 120 | incData[i].t = 0; 121 | incData[i].d = 0; 122 | } 123 | serializeJson(doc, Serial); 124 | Serial.println(); 125 | } 126 | 127 | void loop() { 128 | while (Serial.available()) { 129 | getSerial(); 130 | } 131 | if (newData) { 132 | newData = false; 133 | encodeJSON(); 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /Original_FDRS/FDRS_Gateway/fdrs_config.h: -------------------------------------------------------------------------------- 1 | // To configure your FDRS Gateway: 2 | 3 | // Each device in the system has a unique, one-byte address which 4 | // is assigned to the last digit of its MAC address at startup. 5 | 6 | // Each device is configured to know what the previous and/or next 7 | // device in the line of communication is. 8 | 9 | // The gateway should usually be assigned the address 0x00. 10 | // The PREV_MAC is currently not used, as the gateway responds 11 | // to all packets in the same manner. 12 | 13 | 14 | 15 | // THIS UNIT 16 | #define UNIT_MAC 0x00 17 | #define PREV_MAC 0x01 18 | 19 | //MAC prefix: 20 | #define MAC_PREFIX 0xAA, 0xBB, 0xCC, 0xDD, 0xEE 21 | -------------------------------------------------------------------------------- /Original_FDRS/FDRS_Relay/FDRS_Relay.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // RELAY MODULE 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // Setup instructions located in the "fdrs_config.h" file. 7 | 8 | #if defined(ESP8266) 9 | #include 10 | #include 11 | #elif defined(ESP32) 12 | #include 13 | #include 14 | #include 15 | #endif 16 | #include "fdrs_config.h" 17 | 18 | uint8_t prevAddress[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, PREV_MAC}; 19 | uint8_t selfAddress[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, UNIT_MAC}; 20 | uint8_t nextAddress[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, NEXT_MAC}; 21 | uint8_t outMAC[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, DEFT_MAC}; 22 | uint8_t incMAC[6]; 23 | 24 | uint8_t theData[250]; 25 | uint8_t ln; 26 | bool newData = false; 27 | bool isLocal = false; 28 | void passOn() { 29 | switch (incMAC[5]) { 30 | case PREV_MAC: 31 | outMAC[5] = NEXT_MAC; 32 | break; 33 | case NEXT_MAC: 34 | outMAC[5] = PREV_MAC; 35 | break; 36 | default: 37 | outMAC[5] = DEFT_MAC; 38 | break; 39 | } 40 | if (!isLocal) outMAC[5] = DEFT_MAC; 41 | Serial.print("Packet Received from device: "); 42 | Serial.println(incMAC[5]); 43 | Serial.print("and sending to: "); 44 | Serial.println(outMAC[5]); 45 | esp_now_send(outMAC, (uint8_t *) &theData, ln); 46 | } 47 | 48 | // Set ESP-NOW send and receive callbacks for either ESP8266 or ESP32 49 | #if defined(ESP8266) 50 | void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { 51 | Serial.print("Last Packet Send Status: "); 52 | if (sendStatus == 0) { 53 | Serial.println("Delivery success"); 54 | } 55 | else { 56 | 57 | Serial.println("Delivery fail"); 58 | } 59 | } 60 | void OnDataRecv(uint8_t* mac, uint8_t *incomingData, uint8_t len) { 61 | #elif defined(ESP32) 62 | void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { 63 | Serial.print("Last Packet Send Status:"); 64 | Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail"); 65 | } 66 | void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { 67 | #endif 68 | memcpy(&theData, incomingData, sizeof(theData)); 69 | memcpy(&incMAC, mac, sizeof(incMAC)); 70 | if (memcmp(&incMAC, &selfAddress, 5) != 0) { 71 | isLocal = false; 72 | } else { 73 | isLocal = true; 74 | } 75 | Serial.print("Data received: "); 76 | Serial.println(len); 77 | ln = len; 78 | newData = true; 79 | } 80 | 81 | void setup() { 82 | // Init Serial Monitor 83 | Serial.begin(115200); 84 | // Init WiFi 85 | WiFi.mode(WIFI_STA); 86 | WiFi.disconnect(); 87 | // Init ESP-NOW for either ESP8266 or ESP32 and set MAC address 88 | #if defined(ESP8266) 89 | wifi_set_macaddr(STATION_IF, selfAddress); 90 | if (esp_now_init() != 0) { 91 | return; 92 | } 93 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 94 | esp_now_register_send_cb(OnDataSent); 95 | esp_now_register_recv_cb(OnDataRecv); 96 | // Register peers 97 | esp_now_add_peer(prevAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 98 | esp_now_add_peer(nextAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 99 | #elif defined(ESP32) 100 | esp_wifi_set_mac(WIFI_IF_STA, &selfAddress[0]); 101 | if (esp_now_init() != ESP_OK) { 102 | Serial.println("Error initializing ESP-NOW"); 103 | return; 104 | } 105 | esp_now_register_send_cb(OnDataSent); 106 | esp_now_register_recv_cb(OnDataRecv); 107 | esp_now_peer_info_t peerInfo; 108 | peerInfo.channel = 0; 109 | peerInfo.encrypt = false; 110 | // Register first peer 111 | memcpy(peerInfo.peer_addr, prevAddress, 6); 112 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 113 | Serial.println("Failed to add peer"); 114 | return; 115 | } 116 | // Register second peer 117 | memcpy(peerInfo.peer_addr, nextAddress, 6); 118 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 119 | Serial.println("Failed to add peer"); 120 | return; 121 | } 122 | #endif 123 | 124 | Serial.println(); 125 | Serial.println("FARM DATA RELAY SYSTEM :: Relay Module"); 126 | Serial.println("MAC:" + WiFi.macAddress()); 127 | Serial.print("Previous device: "); 128 | Serial.println(PREV_MAC); 129 | Serial.print("Next device: "); 130 | Serial.println(NEXT_MAC); 131 | Serial.print("Default device: "); 132 | Serial.println(DEFT_MAC); 133 | Serial.println(" "); 134 | } 135 | 136 | void loop() { 137 | if (newData) { 138 | newData = false; 139 | passOn(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /Original_FDRS/FDRS_Relay/fdrs_config.h: -------------------------------------------------------------------------------- 1 | // To configure FDRS: 2 | 3 | // Each device in the system has a unique, one-byte address which 4 | // is assigned to the last digit of its MAC address at startup. 5 | 6 | // Each relay receives data from its pre-programmed "PREV_MAC" device and 7 | // sends the packet verbatim to the address corresponding to "NEXT_MAC". 8 | 9 | // The gateway receives the data and outputs it as a json string over the serial port. 10 | 11 | 12 | // THIS UNIT 13 | #define UNIT_MAC 0x01 14 | 15 | // NEXT UNIT 16 | #define NEXT_MAC 0x00 17 | 18 | // PREVIOUS UNIT 19 | #define PREV_MAC 0x02 20 | 21 | //Packets from unknown MACs are sent here. 22 | #define DEFT_MAC NEXT_MAC 23 | -------------------------------------------------------------------------------- /Original_FDRS/FDRS_Terminal/DataReading.h: -------------------------------------------------------------------------------- 1 | 2 | typedef struct DataReading { 3 | float d; 4 | uint16_t id; 5 | uint8_t t; 6 | 7 | } DataReading; 8 | -------------------------------------------------------------------------------- /Original_FDRS/FDRS_Terminal/FDRS_Terminal.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // TERMINAL MODULE 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // Setup instructions located in the "fdrs_config.h" file. 7 | 8 | #if defined(ESP8266) 9 | #include 10 | #include 11 | #elif defined(ESP32) 12 | #include 13 | #include 14 | #include 15 | #endif 16 | #include "fdrs_config.h" 17 | #include "DataReading.h" 18 | 19 | 20 | uint8_t selfAddress[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, UNIT_MAC}; 21 | uint8_t nextAddress[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, NEXT_MAC}; 22 | uint8_t broadcast_mac[] = {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}; 23 | DataReading incData[31]; 24 | DataReading theData[31]; 25 | DataReading theCommands[31]; 26 | bool newCMD = false; 27 | int wait_time = 0; 28 | int tot_readings = 0; 29 | int tot_commands = 0; 30 | 31 | 32 | #if defined(ESP8266) 33 | void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { 34 | Serial.print("Last Packet Send Status: "); 35 | if (sendStatus == 0) { 36 | Serial.println("Delivery success"); 37 | } 38 | else { 39 | Serial.println("Delivery fail"); 40 | } 41 | } 42 | void OnDataRecv(uint8_t * mac, uint8_t *incomingData, uint8_t len) { 43 | #elif defined(ESP32) 44 | void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { 45 | Serial.print("Last Packet Send Status:"); 46 | Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail"); 47 | } 48 | void OnDataRecv(const uint8_t * mac, const uint8_t *incomingData, int len) { 49 | #endif 50 | uint8_t incMAC[6]; 51 | memcpy(&incData, incomingData, len); 52 | memcpy(&mac, incMAC, 6); 53 | //if (memcmp(&incMAC, &nextAddress, 6) == 0) 54 | int pkt_readings = len / sizeof(DataReading); 55 | Serial.println(":Packet:"); 56 | for (byte i = 0; i < pkt_readings; i++) { 57 | Serial.println("SENSOR ID: " + String(incData[i].id) + " TYPE " + String(incData[i].t) + " DATA " + String(incData[i].d)); 58 | 59 | if (incData[i].t < 200) { 60 | 61 | if (tot_readings >= 250 / sizeof(DataReading)) { 62 | Serial.println("ERROR::Too many sensor readings sent within delay period."); 63 | break; 64 | } 65 | theData[tot_readings] = incData[i]; //Save the current incoming reading to the next open packet slot 66 | ++tot_readings; 67 | } else { 68 | theCommands[tot_commands] = incData[i]; //Save the current incoming reading to the next open packet slot 69 | ++tot_commands; 70 | newCMD = true; 71 | } 72 | 73 | } 74 | } 75 | 76 | void setup() { 77 | // Init Serial 78 | Serial.begin(115200); 79 | // Init WiFi 80 | WiFi.mode(WIFI_STA); 81 | WiFi.disconnect(); 82 | // Init ESP-NOW for either ESP8266 or ESP32 and set MAC address 83 | #if defined(ESP8266) 84 | wifi_set_macaddr(STATION_IF, selfAddress); 85 | if (esp_now_init() != 0) { 86 | return; 87 | } 88 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 89 | esp_now_register_send_cb(OnDataSent); 90 | esp_now_register_recv_cb(OnDataRecv); 91 | // Register peer 92 | esp_now_add_peer(nextAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 93 | esp_now_add_peer(broadcast_mac, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 94 | 95 | #elif defined(ESP32) 96 | esp_wifi_set_mac(WIFI_IF_STA, &selfAddress[0]); 97 | if (esp_now_init() != ESP_OK) { 98 | Serial.println("Error initializing ESP-NOW"); 99 | return; 100 | } 101 | esp_now_register_send_cb(OnDataSent); 102 | esp_now_register_recv_cb(OnDataRecv); 103 | esp_now_peer_info_t peerInfo; 104 | peerInfo.channel = 0; 105 | peerInfo.encrypt = false; 106 | // Register peer 107 | memcpy(peerInfo.peer_addr, nextAddress, 6); 108 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 109 | Serial.println("Failed to add peer"); 110 | return; 111 | } 112 | memcpy(peerInfo.peer_addr, broadcast_mac, 6); 113 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 114 | Serial.println("Failed to add peer"); 115 | return; 116 | } 117 | #endif 118 | Serial.println(); 119 | Serial.println("FARM DATA RELAY SYSTEM :: Terminal Module"); 120 | Serial.println("MAC:" + WiFi.macAddress()); 121 | Serial.print("Next device: "); 122 | Serial.println(NEXT_MAC); 123 | // Serial.println(250 / sizeof(DataReading), DEC); 124 | // Serial.println(sizeof(DataReading), DEC); 125 | } 126 | 127 | void loop() { 128 | if (newCMD) sendCmd(); 129 | if (millis() > wait_time) { 130 | wait_time = wait_time + DELAY; 131 | if (tot_readings != 0) passForward(); 132 | } 133 | } 134 | 135 | void passForward() { 136 | Serial.println("Passing Forward"); 137 | esp_now_send(nextAddress, (uint8_t *) &theData, sizeof(DataReading)*tot_readings); 138 | tot_readings = 0; 139 | for (int i = 0; i < 250 / sizeof(DataReading); i++) { 140 | theData[i].d = 0; 141 | theData[i].id = 0; 142 | theData[i].t = 0; 143 | } 144 | } 145 | 146 | void sendCmd() { 147 | newCMD = false; 148 | esp_now_send(broadcast_mac, (uint8_t *) &theCommands, tot_commands* sizeof(DataReading)); 149 | tot_commands = 0; 150 | 151 | } 152 | -------------------------------------------------------------------------------- /Original_FDRS/FDRS_Terminal/fdrs_config.h: -------------------------------------------------------------------------------- 1 | // To configure FDRS: 2 | 3 | // Uncomment the code corresponding to the unit you are configuring, 4 | // then uncomment the code corresponding to the unit you would like 5 | // to be previous and/or next in the line of communication. 6 | 7 | // Each device in the system has a unique, one-byte address which 8 | // is assigned to the last digit of its MAC address at startup. 9 | 10 | // Each device is configured to know what the previous and/or next 11 | // device in the line of communication is. 12 | 13 | // The terminal is considered the "first" device, which can be addressed 14 | // to a relay or the gateway. 15 | 16 | // Each relay receives data from its pre-programmed "PREV_MAC" device and 17 | // sends the packet verbatim to the address corresponding to "NEXT_MAC". 18 | 19 | // The gateway receives the data and outputs it as a json string over the serial port. 20 | 21 | #define DELAY 15000 22 | 23 | // THIS UNIT 24 | #define UNIT_MAC 0x01 25 | 26 | // NEXT UNIT 27 | #define NEXT_MAC 0x00 28 | -------------------------------------------------------------------------------- /Original_FDRS/Front-End_Modules/FDRS_Blynk/DataReading.h: -------------------------------------------------------------------------------- 1 | 2 | typedef struct DataReading { 3 | float d; 4 | uint16_t id; 5 | uint8_t t; 6 | 7 | } DataReading; 8 | -------------------------------------------------------------------------------- /Original_FDRS/Front-End_Modules/FDRS_Blynk/FDRS_Blynk.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // BLYNK FRONT-END MODULE 4 | // Uses JSON data from the serial port to set Blynk variables. 5 | // 6 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 7 | // This code was written for the now-outdated version of Blynk, and is mostly here for reference. 8 | // 9 | 10 | #define STASSID "" //Your SSID 11 | #define STAPSK "" //Your Password 12 | #define BLKAUTH "" //Your Blynk auth code 13 | 14 | #define DELAY 30000 15 | 16 | #if defined(ESP8266) 17 | #include 18 | #include 19 | #elif defined(ESP32) 20 | #include 21 | #include 22 | #endif 23 | #include 24 | #include "DataReading.h" 25 | 26 | BlynkTimer timer; 27 | WidgetTerminal terminal(V69); 28 | 29 | DataReading allData[256]; 30 | int wait_time = DELAY; 31 | char auth[] = BLKAUTH; 32 | const char* ssid = STASSID; 33 | const char* password = STAPSK; 34 | bool is_new[255]; 35 | int new_readings; 36 | bool term_flag; 37 | String the_command; 38 | 39 | BLYNK_WRITE(V69) 40 | { 41 | Serial.println("blynkwrite"); 42 | the_command = param.asStr(); 43 | term_flag = true; 44 | } 45 | 46 | void setup() { 47 | Serial.begin(115200); 48 | WiFi.mode(WIFI_STA); 49 | Serial.println(); 50 | Serial.println("FDRS Blynk Module"); 51 | Serial.println("Connect to WiFi please?"); 52 | Blynk.begin(auth, ssid, password); 53 | // Blynk.config(auth); 54 | // Blynk.connectWiFi(ssid, password); 55 | Serial.println("Thanks!"); 56 | //terminal.clear(); 57 | timer.setInterval(30000L, updateBlynk); 58 | Blynk.run(); 59 | terminal.println("Hello, world!"); 60 | terminal.flush(); 61 | } 62 | 63 | void loop() { 64 | 65 | while (Serial.available()) { 66 | Serial.println("waiting for tilda"); 67 | if (Serial.read() == '~') { //Waits for '~', then parses following data 68 | getSerial(); 69 | Serial.println("getSerial done"); 70 | break; 71 | } 72 | } 73 | // if (millis() > wait_time) { 74 | // wait_time = wait_time + DELAY; 75 | // if (Blynk.connected()) { 76 | // updateBlynk(); 77 | // Serial.println("blynk updated"); 78 | // } 79 | // } 80 | if (term_flag) { 81 | parseBlynkTerm(the_command); 82 | term_flag = false; 83 | } 84 | Blynk.run(); 85 | timer.run(); 86 | } 87 | 88 | void getSerial() { 89 | String incomingString = Serial.readString(); 90 | StaticJsonDocument<3072> doc; 91 | Serial.println("Received: " + incomingString); 92 | DeserializationError error = deserializeJson(doc, incomingString); 93 | if (error) { // Test if parsing succeeds. 94 | Serial.print(F("deserializeJson() failed: ")); 95 | Serial.println(error.f_str()); 96 | return; 97 | } else { 98 | for (int i = 0; i < doc.size(); i++) { 99 | Serial.println(doc.size()); 100 | DataReading newData; 101 | newData.id = doc[i]["id"]; 102 | newData.t = doc[i]["type"]; 103 | newData.d = doc[i]["data"]; 104 | allData[newData.id] = newData; 105 | is_new[newData.id] = true; 106 | Serial.println("SENSOR ID: " + String(newData.id) + " TYPE " + String(newData.t) + " DATA " + String(newData.d)); 107 | yield(); 108 | } 109 | } 110 | } 111 | 112 | void updateBlynk() { 113 | Serial.println("Updateblynk"); 114 | for (int i = 0; i < 127; i++) { 115 | if (is_new[i] == true) { 116 | Serial.println("abt to write"); 117 | Blynk.virtualWrite(i, allData[i].d); 118 | is_new[i] = false; 119 | yield(); 120 | } 121 | } 122 | } 123 | void parseBlynkTerm(String command) { 124 | int ind1 = command.indexOf(" "); 125 | String arg1 = command.substring(0, ind1); 126 | if (arg1.equalsIgnoreCase(String("SET"))) { 127 | int ind2 = command.indexOf(" ", ind1 + 1); 128 | String arg2 = command.substring(ind1 + 1, ind2); 129 | String arg3 = command.substring(ind2 + 1, command.length()); 130 | terminal.println("ARG1:" + arg1); 131 | terminal.println("ARG2:" + arg2); 132 | terminal.println("ARG3:" + arg3); 133 | terminal.flush(); 134 | DataReading newCMD; 135 | newCMD.id = arg2.toInt(); 136 | newCMD.t = 201; 137 | newCMD.d = arg3.toFloat(); 138 | allData[newCMD.id] = newCMD; 139 | StaticJsonDocument<2048> doc; 140 | doc[0]["id"] = newCMD.id; 141 | doc[0]["type"] = newCMD.t; 142 | doc[0]["data"] = newCMD.d; 143 | Serial.write('~'); 144 | serializeJson(doc, Serial); 145 | Serial.println(); 146 | } else if (arg1.equalsIgnoreCase(String("GET"))) { 147 | String arg2 = command.substring(ind1 + 1, command.length()); 148 | terminal.println("DataReading " + arg2 + " :: " + String(allData[arg2.toInt()].d)); 149 | terminal.flush(); 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /Original_FDRS/README.md: -------------------------------------------------------------------------------- 1 | #

Ye Olde Farm Data Relay System 2 | #####

[***In loving memory of Gay Holman, an extraordinary woman.***](https://www.facebook.com/CFECI/posts/2967989419953119) ##### 3 | 4 | ### This is the first version of my project, and is left here only for reference. 5 | 6 | The Farm Data Relay System is an easy way to link remote sensors to the internet without the need for WiFi. It uses the ESP-NOW protocol on widely availabe ESP32 and ESP8266 microcontroller boards, and can be used to collect and transmit sensor data in situations where it would be too difficult or energy-consuming to provide full WiFi coverage. 7 | 8 | A major consideration has been to make the system straight-forward and easy to set up, since the primary user may not be very familiar with Arduino. Using an assigned MAC address scheme allows for the whole system to be configured by setting just a handful of values in code. 9 | 10 | Other than the nodes, each device in the system has a one-byte address. At boot, each device changes its MAC address to "AA:BB:CC:DD:EE:xx" where xx is the device's address identifier. Nodes can send their data to a terminal, a relay, or directly to a gateway. Each of these devices can be connected as needed in a chain that leads to a gateway, where the data is handed off to another system. 11 | 12 | ## Getting Started 13 | To get started using FDRS with Node-Red, you'll first need to flash an ESP32 or 8266 device with the FDRS_Gateway sketch. Next, connect the device via serial port to a computer running Node-Red. You can then use the serialport node connected to a JSON node, and finally a split node to get your data ready to use. You can find the basic flow examples in the 'NodeRed-Basic.json' file. If you are using a usb-serial port, keep in mind that you may have to change the selected port after plugging in your device, or de-select the port when trying to flash the device from another program. 14 | 15 | As you flash each sensor, you'll need to change the UNIT_ID to a unique integer (0-65535). For some sensors, you'll also need to adjust pin assignments and install libraries. 16 | By default, all sensors have GTWY_MAC set to 0x00. You'll only need to change this if you are going to use a relay to extend the system's range. 17 | 18 | If you need to get data from sensors that are out of reach of the gateway, place an FDRS_Relay device near the edge of the gateway's range. Flash this device with the default parameters, and then flash the sensors with GTWY_MAC set to 0x01. They will now send their data to the relay, which in turn sends it to the gateway. 19 | 20 | ## Sensors 21 | ``` 22 | typedef struct DataReading { 23 | float d; 24 | uint16_t id; 25 | uint8_t t; 26 | } DataReading; 27 | ``` 28 | Each sensor in the system sends its data over ESP-NOW as a float 'd' inside of a structure called a DataReading. Its global sensor address is represented by an integer 'id', and each type is represented by a single byte 't'. If sensors need to send multiple types of readings (ex: temp and humidity), then they are sent in an array of DataReadings. In this case each reading will share an id, but be differentiated by its type. 29 | 30 | ## Terminal 31 | A terminal is a device that recieves data from the nodes and aggregates it into a larger array. The larger array is then periodically sent forward through the system to the gateway. The time between sends must be short enough so as not to exceed the maximum legnth of an ESP-NOW packet with DataReadings, which is 31. 32 | 33 | 34 | ## Relays 35 | Relays are absolute ESP-NOW repeaters. They are programmed with the address of the previous and next device, and when data is received from one, it delivers it to the other. The "DEFT_MAC" setting defines where a relay will send an incoming packet with an unknown address. This can be used to direct sensor packets either to the terminal or the gateway. 36 | 37 | In the FDRS, one can place as many relays as needed to span the distance to the gateway, which is the final device in the system. 38 | 39 | ## Gateway 40 | The gateway takes the packet of DataReadings and interprets it into json format, then outputs it over the serial port. From here it can be collected by another microcontroller or a Raspberry Pi front-end to be sent to the cloud for further processing and storage. 41 | 42 | 43 | 44 | 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #

Farm Data Relay System 2 | #####

[***In loving memory of Gay Holman, an extraordinary woman.***](https://www.facebook.com/CFECI/posts/2967989419953119) ##### 3 | 4 | The Farm Data Relay System is an easy way to collect data from remote sensors without relying on WiFi. It is based around the ESP-NOW protocol, which is readily available on ESP32 and ESP8266 microcontroller boards. The system can be used to collect and transmit sensor data in situations where it would be too difficult or energy-consuming to provide full WiFi coverage. 5 | 6 | Using an assigned MAC address scheme allows for the whole system to be configured by setting just a handful of values in code. Every wireless gateway is assigned a single-byte identifier, known as the UNIT_MAC. This, along with a set, 5-byte prefix is assigned to the MAC address of the ESP's radio at boot. 7 | 8 | Gateways can be configured to send an ESP-NOW transmission to the serial port using JSON, another ESP-NOW gateway, or broadcast it via LoRa PHY. They can also be configured to transmit incoming serial packets over MQTT, ESP-NOW, or LoRa PHY. 9 | 10 | ## Getting Started 11 | To use FDRS with Node-Red and MQTT you'll need two ESP devices (gateways) that are connected via UART, plus additional ESP or LoRa devices with sensors connected. 12 | 13 | ### Sensors 14 | The [Sensor2000](https://github.com/timmbogner/Farm-Data-Relay-System/tree/main/FDRS_Sensor2000) sketch is an example of how a sensor is configured in FDRS. The configuration parameters and functional code are located in the 'fdrs_sensor.h' file, so the user is free to use the main sketch for individual sensor code. 15 | 16 | ### Gateways 17 | Gateways are programmed using the instructions [found with the Gateway2000 sketch](https://github.com/timmbogner/Farm-Data-Relay-System/tree/main/FDRS_Gateway2000). 18 | 19 | ### Front-end 20 | The Node-RED front-end can be set up with these nodes to format and send the data to InfluxDB: 21 | ``` 22 | [{"id":"66d36c0f.cedf94","type":"influxdb out","z":"d7346a99.716ef8","influxdb":"905dd357.34717","name":"","measurement":"DataReading","precision":"","retentionPolicy":"","database":"database","precisionV18FluxV20":"ms","retentionPolicyV18Flux":"","org":"the_organization","bucket":"bkt","x":760,"y":240,"wires":[]},{"id":"93e9822a.3ad59","type":"mqtt in","z":"d7346a99.716ef8","name":"","topic":"esp/fdrs","qos":"2","datatype":"auto","broker":"c513f7e9.760658","nl":false,"rap":true,"rh":0,"x":170,"y":220,"wires":[["d377f9e0.faef98"]]},{"id":"d377f9e0.faef98","type":"json","z":"d7346a99.716ef8","name":"","property":"payload","action":"obj","pretty":false,"x":290,"y":220,"wires":[["ca383562.4014e8"]]},{"id":"ca383562.4014e8","type":"split","z":"d7346a99.716ef8","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":410,"y":220,"wires":[["6eaba8dd.429e38"]]},{"id":"6eaba8dd.429e38","type":"function","z":"d7346a99.716ef8","name":"Fields","func":"msg.payload = [{\n data: msg.payload.data\n},{\n id: msg.payload.id,\n type: msg.payload.type\n}]\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":530,"y":220,"wires":[["296d0f4b.37a46","66d36c0f.cedf94"]]},{"id":"296d0f4b.37a46","type":"debug","z":"d7346a99.716ef8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":670,"y":200,"wires":[]},{"id":"905dd357.34717","type":"influxdb","hostname":"127.0.0.1","port":"8086","protocol":"http","database":"database","name":"","usetls":false,"tls":"d50d0c9f.31e858","influxdbVersion":"2.0","url":"http://localhost:8086","rejectUnauthorized":true},{"id":"c513f7e9.760658","type":"mqtt-broker","name":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"sessionExpiry":""},{"id":"d50d0c9f.31e858","type":"tls-config","name":"","cert":"","key":"","ca":"","certname":"","keyname":"","caname":"","servername":"","verifyservercert":false}] 23 | ``` 24 | The following nodes do the same as above, but I added the ability to add an alias to each sensor ID via the dashboard using globals. It could use improvement, but might be helpful to some: 25 | ``` 26 | [{"id":"66d36c0f.cedf94","type":"influxdb out","z":"d7346a99.716ef8","influxdb":"905dd357.34717","name":"","measurement":"DataReading","precision":"","retentionPolicy":"","database":"database","precisionV18FluxV20":"ms","retentionPolicyV18Flux":"","org":"the_organization","bucket":"bkt","x":1160,"y":300,"wires":[]},{"id":"93e9822a.3ad59","type":"mqtt in","z":"d7346a99.716ef8","name":"","topic":"esp/fdrs","qos":"2","datatype":"auto","broker":"c513f7e9.760658","nl":false,"rap":true,"rh":0,"x":270,"y":300,"wires":[["d377f9e0.faef98"]]},{"id":"d377f9e0.faef98","type":"json","z":"d7346a99.716ef8","name":"","property":"payload","action":"obj","pretty":false,"x":390,"y":300,"wires":[["ca383562.4014e8"]]},{"id":"ca383562.4014e8","type":"split","z":"d7346a99.716ef8","name":"","splt":"\\n","spltType":"str","arraySplt":1,"arraySpltType":"len","stream":false,"addname":"","x":530,"y":300,"wires":[["f9edcceb.225e8"]]},{"id":"6eaba8dd.429e38","type":"function","z":"d7346a99.716ef8","name":"Fields","func":"msg.payload = [{\n data: msg.payload.data\n},{\n id: msg.payload.id,\n type: msg.payload.type,\n alias: msg.payload.alias,\n model: msg.payload.model,\n indoor: msg.payload.indoor,\n outdoor: msg.payload.outdoor\n}]\n\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":810,"y":300,"wires":[["66d36c0f.cedf94","296d0f4b.37a46"]]},{"id":"296d0f4b.37a46","type":"debug","z":"d7346a99.716ef8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"payload","targetType":"msg","statusVal":"","statusType":"auto","x":1030,"y":240,"wires":[]},{"id":"edec396f.607608","type":"ui_form","z":"d7346a99.716ef8","name":"","label":"Enter ID and Alias","group":"361e2c11.6c24e4","order":0,"width":0,"height":0,"options":[{"label":"Device ID","value":"id","type":"number","required":true,"rows":null},{"label":"Desired Alias","value":"alias","type":"text","required":true,"rows":null},{"label":"Sensor Type","value":"model","type":"text","required":false,"rows":null},{"label":"Indoor","value":"indoor","type":"checkbox","required":false,"rows":null},{"label":"Outdoor","value":"outdoor","type":"checkbox","required":false,"rows":null}],"formValue":{"id":"","alias":"","model":"","indoor":false,"outdoor":false},"payload":"","submit":"submit","cancel":"cancel","topic":"topic","topicType":"msg","splitLayout":"","x":310,"y":380,"wires":[["24844b21.4e0f24"]]},{"id":"391507dd.567968","type":"debug","z":"d7346a99.716ef8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","statusVal":"","statusType":"auto","x":810,"y":240,"wires":[]},{"id":"f9edcceb.225e8","type":"function","z":"d7346a99.716ef8","name":"","func":"var atts = global.get(\"attribute_list[\" + msg.payload.id + \"]\" );\nif(atts !== undefined){\nmsg.payload.alias = atts.alias;\nmsg.payload.model = atts.model;\nmsg.payload.indoor = atts.indoor;\nmsg.payload.outdoor = atts.outdoor;\n}\n\nreturn msg;\n","outputs":1,"noerr":0,"initialize":"","finalize":"","libs":[],"x":660,"y":300,"wires":[["391507dd.567968","6eaba8dd.429e38"]]},{"id":"c27b4f12.71867","type":"debug","z":"d7346a99.716ef8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":670,"y":380,"wires":[]},{"id":"24844b21.4e0f24","type":"change","z":"d7346a99.716ef8","name":"","rules":[{"t":"set","p":"attribute_list[msg.payload.id].alias","pt":"global","to":"payload.alias","tot":"msg"},{"t":"set","p":"attribute_list[msg.payload.id].model","pt":"global","to":"payload.model","tot":"msg"},{"t":"set","p":"attribute_list[msg.payload.id].indoor","pt":"global","to":"payload.indoor","tot":"msg"},{"t":"set","p":"attribute_list[msg.payload.id].outdoor","pt":"global","to":"payload.outdoor","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":520,"y":380,"wires":[["c27b4f12.71867"]]},{"id":"905dd357.34717","type":"influxdb","hostname":"127.0.0.1","port":"8086","protocol":"http","database":"database","name":"","usetls":false,"tls":"d50d0c9f.31e858","influxdbVersion":"2.0","url":"http://localhost:8086","rejectUnauthorized":true},{"id":"c513f7e9.760658","type":"mqtt-broker","name":"","broker":"localhost","port":"1883","clientid":"","usetls":false,"protocolVersion":"4","keepalive":"60","cleansession":true,"birthTopic":"","birthQos":"0","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willPayload":"","willMsg":{},"sessionExpiry":""},{"id":"361e2c11.6c24e4","type":"ui_group","name":"Configuration","tab":"8a89d1e.eb12c3","order":1,"disp":true,"width":"10","collapse":false},{"id":"d50d0c9f.31e858","type":"tls-config","name":"","cert":"","key":"","ca":"","certname":"","keyname":"","caname":"","servername":"","verifyservercert":false},{"id":"8a89d1e.eb12c3","type":"ui_tab","name":"Farm Data Relay System","icon":"dashboard","order":1,"disabled":false,"hidden":false}] 27 | ``` 28 | 29 | ![Basic](/FDRS_Gateway2000/Basic_Setup.png) 30 | ![Advanced](/FDRS_Gateway2000/Advanced_Setup.png) 31 | ### Sensors 32 | ``` 33 | typedef struct DataReading { 34 | float d; 35 | uint16_t id; 36 | uint8_t t; 37 | } DataReading; 38 | ``` 39 | Each sensor in the system sends its data over ESP-NOW as a float 'd' inside of a structure called a DataReading. Its global sensor address is represented by an integer 'id', and each type is represented by a single byte 't'. If a sensor module needs to send multiple types of readings, then they are sent in an array of DataReadings. A single DataReading.id may have readings of multiple types associated with it. 40 | ## Future Plans 41 | A few things that I intend to add are: 42 | - A way for the sensors to seek out a nearby gateway and pair with it. 43 | - The ability to send data in reverse, and have nodes to control irrigation, ventilation, and LED illumination. This will be acheived using a similar pairing technique to the above. 44 | - More sensor sketches! If you have designed any open source sensor modules for ESP32 or 8266, please contact me and I will provide a link and/or code for your device in this repo. 45 | - I am going to look into adding an option to change the channel that the system runs on, so that WiFi and ESP-NOW might run together harmoniously. 46 | - Support for several new devices and protocols: ethernet, nRF24L01, 4G LTE, and the E5 LoRa module from Seeed Studio. 47 | - Some ability to compress data for more efficient LoRa usage and to avoid using floats. Better documentation/development of the DataReading 'type' attribute will come with this. 48 | 49 | ## Thank you 50 | ...very much for checking out my project! I truly appreciate everyone across the net who has reached out with assistance and encouragement. If you have any questions, comments, or issues please feel free to contact me at timmbogner@gmail.com. 51 | 52 | If you have any extra money, **please consider [supporting me](https://www.buymeacoffee.com/TimmB).** I'm a farmer, landscaper, and grocery store clerk by occupation, so donations would help me to spend more time developing farm gadgets. 53 | 54 | Development of this project would not have been possible without the gracious support of my former employer, **Sola Gratia Farm** of **Urbana, IL, USA**. Sola Gratia is a community-based farm dedicated to growing high-quality produce and sharing it with those in need. Thank you! 55 | 56 | A huge thanks to the ever-instructional [**Andreas Spiess**](https://www.youtube.com/channel/UCu7_D0o48KbfhpEohoP7YSQ). 57 | 58 | [**Random Nerd Tutorials**](https://randomnerdtutorials.com/) is also an indispensable source of ESP knowledge. 59 | -------------------------------------------------------------------------------- /Sensors/AHT20_fdrs/AHT20_fdrs.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // AHT20 SENSOR MODULE 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | 7 | #include 8 | #include "fdrs_sensor.h" 9 | 10 | Adafruit_AHTX0 aht; 11 | 12 | void setup() { 13 | Serial.begin(115200); 14 | beginFDRS(); 15 | if (! aht.begin()) { 16 | Serial.println("Could not find AHT? Check wiring"); 17 | while (1) delay(10); 18 | } 19 | } 20 | 21 | void loop() { 22 | sensors_event_t humidity, temp; 23 | aht.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data 24 | loadFDRS(temp.temperature, TEMP_T); 25 | loadFDRS(humidity.relative_humidity, HUMIDITY_T); 26 | sendFDRS(); 27 | sleepFDRS(60); //Sleep time in seconds 28 | } 29 | -------------------------------------------------------------------------------- /Sensors/AHT20_fdrs/fdrs_sensor.h: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // "fdrs_sensor.h" 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // 7 | #define READING_ID 8 //Unique ID for sensor module 8 | #define GTWY_MAC 0x00 //Address of the nearest gateway 9 | 10 | #define POWER_CTRL 14 11 | #define MAC_PREFIX 0xAA, 0xBB, 0xCC, 0xDD, 0xEE 12 | #define DEEP_SLEEP 13 | #define USE_ESPNOW 14 | 15 | //#define USE_LORA 16 | #define SCK 5 17 | #define MISO 19 18 | #define MOSI 27 19 | #define SS 18 20 | #define RST 14 21 | #define DIO0 26 22 | //433E6 for Asia 23 | //866E6 for Europe 24 | //915E6 for North America 25 | #define BAND 915E6 26 | 27 | typedef struct __attribute__((packed)) DataReading { 28 | float d; 29 | uint16_t id; 30 | uint8_t t; 31 | 32 | } DataReading; 33 | 34 | #define STATUS_T 0 // Status 35 | #define TEMP_T 1 // Temperature 36 | #define TEMP2_T 2 // Temperature #2 37 | #define HUMIDITY_T 3 // Relative Humidity 38 | #define PRESSURE_T 4 // Atmospheric Pressure 39 | #define LIGHT_T 5 // Light (lux) 40 | #define SOIL_T 6 // Soil Moisture 41 | #define SOIL2_T 7 // Soil Moisture #2 42 | #define SOILR_T 8 // Soil Resistance 43 | #define SOILR2_T 9 // Soil Resistance #2 44 | #define OXYGEN_T 10 // Oxygen 45 | #define CO2_T 11 // Carbon Dioxide 46 | #define WINDSPD_T 12 // Wind Speed 47 | #define WINDHDG_T 13 // Wind Direction 48 | #define RAINFALL_T 14 // Rainfall 49 | #define MOTION_T 15 // Motion 50 | #define VOLTAGE_T 16 // Voltage 51 | #define VOLTAGE2_T 17 // Voltage #2 52 | #define CURRENT_T 18 // Current 53 | #define CURRENT2_T 19 // Current #2 54 | #define IT_T 20 // Iterations 55 | 56 | 57 | #if defined(ESP8266) 58 | #include 59 | #include 60 | #elif defined(ESP32) 61 | #include 62 | #include 63 | #include 64 | #endif 65 | #include 66 | 67 | 68 | const uint16_t espnow_size = 250 / sizeof(DataReading); 69 | uint8_t gatewayAddress[] = {MAC_PREFIX, GTWY_MAC}; 70 | uint8_t gtwyAddress[] = {gatewayAddress[3], gatewayAddress[4], GTWY_MAC}; 71 | uint8_t LoRaAddress[] = {0x42, 0x00}; 72 | 73 | 74 | uint32_t wait_time = 0; 75 | DataReading fdrsData[espnow_size]; 76 | uint8_t data_count = 0; 77 | 78 | void beginFDRS() { 79 | #ifdef USE_ESPNOW 80 | WiFi.mode(WIFI_STA); 81 | WiFi.disconnect(); 82 | #ifdef POWER_CTRL 83 | pinMode(POWER_CTRL, OUTPUT); 84 | digitalWrite(POWER_CTRL, 1); 85 | #endif 86 | // Init ESP-NOW for either ESP8266 or ESP32 and set MAC address 87 | #if defined(ESP8266) 88 | if (esp_now_init() != 0) { 89 | return; 90 | } 91 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 92 | // Register peers 93 | esp_now_add_peer(gatewayAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 94 | #elif defined(ESP32) 95 | if (esp_now_init() != ESP_OK) { 96 | Serial.println("Error initializing ESP-NOW"); 97 | return; 98 | } 99 | esp_now_peer_info_t peerInfo; 100 | peerInfo.ifidx = WIFI_IF_STA; 101 | peerInfo.channel = 0; 102 | peerInfo.encrypt = false; 103 | // Register first peer 104 | memcpy(peerInfo.peer_addr, gatewayAddress, 6); 105 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 106 | Serial.println("Failed to add peer"); 107 | return; 108 | } 109 | #endif 110 | #endif 111 | #ifdef USE_LORA 112 | #ifndef __AVR__ 113 | SPI.begin(SCK, MISO, MOSI, SS); 114 | #endif 115 | LoRa.setPins(SS, RST, DIO0); 116 | if (!LoRa.begin(BAND)) { 117 | while (1); 118 | } 119 | #endif 120 | } 121 | void transmitLoRa(uint8_t* mac, DataReading * packet, uint8_t len) { 122 | #ifdef USE_LORA 123 | uint8_t pkt[5 + (len * sizeof(DataReading))]; 124 | memcpy(&pkt, mac, 3); 125 | memcpy(&pkt[3], &LoRaAddress, 2); 126 | memcpy(&pkt[5], packet, len * sizeof(DataReading)); 127 | LoRa.beginPacket(); 128 | LoRa.write((uint8_t*)&pkt, sizeof(pkt)); 129 | LoRa.endPacket(); 130 | #endif 131 | } 132 | void sendFDRS() { 133 | #ifdef USE_ESPNOW 134 | esp_now_send(gatewayAddress, (uint8_t *) &fdrsData, data_count * sizeof(DataReading)); 135 | delay(5); 136 | #endif 137 | #ifdef USE_LORA 138 | transmitLoRa(gtwyAddress, fdrsData, data_count); 139 | #endif 140 | data_count = 0; 141 | } 142 | void loadFDRS(float d, uint8_t t) { 143 | if (data_count > espnow_size) sendFDRS(); 144 | DataReading dr; 145 | dr.id = READING_ID; 146 | dr.t = t; 147 | dr.d = d; 148 | fdrsData[data_count] = dr; 149 | data_count++; 150 | 151 | } 152 | void sleepFDRS(int sleep_time) { 153 | #ifdef DEEP_SLEEP 154 | #ifdef ESP32 155 | esp_sleep_enable_timer_wakeup(sleep_time * 1000000); 156 | esp_deep_sleep_start(); 157 | #endif 158 | #ifdef ESP8266 159 | ESP.deepSleep(sleep_time * 1000000); 160 | #endif 161 | #endif 162 | delay(sleep_time * 1000); 163 | 164 | } 165 | -------------------------------------------------------------------------------- /Sensors/BME280_fdrs/BME280_fdrs.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // BME280 SENSOR MODULE 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | 7 | #include 8 | #include "fdrs_sensor.h" 9 | 10 | Adafruit_BME280 bme; 11 | 12 | void setup() { 13 | //Serial.begin(115200); 14 | beginFDRS(); 15 | while (!bme.begin(0x76)) { 16 | //Serial.println("BME not initializing!"); 17 | delay(10); 18 | } 19 | } 20 | 21 | void loop() { 22 | loadFDRS(bme.readTemperature(), TEMP_T); 23 | loadFDRS(bme.readHumidity(), HUMIDITY_T); 24 | loadFDRS(bme.readPressure() / 100.0F, PRESSURE_T); 25 | sendFDRS(); 26 | sleepFDRS(60); //Sleep time in seconds 27 | } 28 | -------------------------------------------------------------------------------- /Sensors/BME280_fdrs/fdrs_sensor.h: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // "fdrs_sensor.h" 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // 7 | #define READING_ID 7 //Unique ID for sensor module 8 | #define GTWY_MAC 0x00 //Address of the nearest gateway 9 | 10 | #define POWER_CTRL 14 11 | #define MAC_PREFIX 0xAA, 0xBB, 0xCC, 0xDD, 0xEE 12 | #define DEEP_SLEEP 13 | #define USE_ESPNOW 14 | 15 | //#define USE_LORA 16 | #define SCK 5 17 | #define MISO 19 18 | #define MOSI 27 19 | #define SS 18 20 | #define RST 14 21 | #define DIO0 26 22 | //433E6 for Asia 23 | //866E6 for Europe 24 | //915E6 for North America 25 | #define BAND 915E6 26 | 27 | typedef struct __attribute__((packed)) DataReading { 28 | float d; 29 | uint16_t id; 30 | uint8_t t; 31 | 32 | } DataReading; 33 | 34 | #define STATUS_T 0 // Status 35 | #define TEMP_T 1 // Temperature 36 | #define TEMP2_T 2 // Temperature #2 37 | #define HUMIDITY_T 3 // Relative Humidity 38 | #define PRESSURE_T 4 // Atmospheric Pressure 39 | #define LIGHT_T 5 // Light (lux) 40 | #define SOIL_T 6 // Soil Moisture 41 | #define SOIL2_T 7 // Soil Moisture #2 42 | #define SOILR_T 8 // Soil Resistance 43 | #define SOILR2_T 9 // Soil Resistance #2 44 | #define OXYGEN_T 10 // Oxygen 45 | #define CO2_T 11 // Carbon Dioxide 46 | #define WINDSPD_T 12 // Wind Speed 47 | #define WINDHDG_T 13 // Wind Direction 48 | #define RAINFALL_T 14 // Rainfall 49 | #define MOTION_T 15 // Motion 50 | #define VOLTAGE_T 16 // Voltage 51 | #define VOLTAGE2_T 17 // Voltage #2 52 | #define CURRENT_T 18 // Current 53 | #define CURRENT2_T 19 // Current #2 54 | #define IT_T 20 // Iterations 55 | 56 | 57 | #if defined(ESP8266) 58 | #include 59 | #include 60 | #elif defined(ESP32) 61 | #include 62 | #include 63 | #include 64 | #endif 65 | #include 66 | 67 | 68 | const uint16_t espnow_size = 250 / sizeof(DataReading); 69 | uint8_t gatewayAddress[] = {MAC_PREFIX, GTWY_MAC}; 70 | uint8_t gtwyAddress[] = {gatewayAddress[3], gatewayAddress[4], GTWY_MAC}; 71 | uint8_t LoRaAddress[] = {0x42, 0x00}; 72 | 73 | 74 | uint32_t wait_time = 0; 75 | DataReading fdrsData[espnow_size]; 76 | uint8_t data_count = 0; 77 | 78 | void beginFDRS() { 79 | #ifdef USE_ESPNOW 80 | WiFi.mode(WIFI_STA); 81 | WiFi.disconnect(); 82 | #ifdef POWER_CTRL 83 | pinMode(POWER_CTRL, OUTPUT); 84 | digitalWrite(POWER_CTRL, 1); 85 | #endif 86 | // Init ESP-NOW for either ESP8266 or ESP32 and set MAC address 87 | #if defined(ESP8266) 88 | if (esp_now_init() != 0) { 89 | return; 90 | } 91 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 92 | // Register peers 93 | esp_now_add_peer(gatewayAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 94 | #elif defined(ESP32) 95 | if (esp_now_init() != ESP_OK) { 96 | Serial.println("Error initializing ESP-NOW"); 97 | return; 98 | } 99 | esp_now_peer_info_t peerInfo; 100 | peerInfo.ifidx = WIFI_IF_STA; 101 | peerInfo.channel = 0; 102 | peerInfo.encrypt = false; 103 | // Register first peer 104 | memcpy(peerInfo.peer_addr, gatewayAddress, 6); 105 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 106 | Serial.println("Failed to add peer"); 107 | return; 108 | } 109 | #endif 110 | #endif 111 | #ifdef USE_LORA 112 | #ifndef __AVR__ 113 | SPI.begin(SCK, MISO, MOSI, SS); 114 | #endif 115 | LoRa.setPins(SS, RST, DIO0); 116 | if (!LoRa.begin(BAND)) { 117 | while (1); 118 | } 119 | #endif 120 | } 121 | void transmitLoRa(uint8_t* mac, DataReading * packet, uint8_t len) { 122 | #ifdef USE_LORA 123 | uint8_t pkt[5 + (len * sizeof(DataReading))]; 124 | memcpy(&pkt, mac, 3); 125 | memcpy(&pkt[3], &LoRaAddress, 2); 126 | memcpy(&pkt[5], packet, len * sizeof(DataReading)); 127 | LoRa.beginPacket(); 128 | LoRa.write((uint8_t*)&pkt, sizeof(pkt)); 129 | LoRa.endPacket(); 130 | #endif 131 | } 132 | void sendFDRS() { 133 | #ifdef USE_ESPNOW 134 | esp_now_send(gatewayAddress, (uint8_t *) &fdrsData, data_count * sizeof(DataReading)); 135 | delay(5); 136 | #endif 137 | #ifdef USE_LORA 138 | transmitLoRa(gtwyAddress, fdrsData, data_count); 139 | #endif 140 | data_count = 0; 141 | } 142 | void loadFDRS(float d, uint8_t t) { 143 | if (data_count > espnow_size) sendFDRS(); 144 | DataReading dr; 145 | dr.id = READING_ID; 146 | dr.t = t; 147 | dr.d = d; 148 | fdrsData[data_count] = dr; 149 | data_count++; 150 | 151 | } 152 | void sleepFDRS(int sleep_time) { 153 | #ifdef DEEP_SLEEP 154 | #ifdef ESP32 155 | esp_sleep_enable_timer_wakeup(sleep_time * 1000000); 156 | esp_deep_sleep_start(); 157 | #endif 158 | #ifdef ESP8266 159 | ESP.deepSleep(sleep_time * 1000000); 160 | #endif 161 | #endif 162 | delay(sleep_time * 1000); 163 | 164 | } 165 | -------------------------------------------------------------------------------- /Sensors/BMP280_fdrs/BMP280_fdrs.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // BMP280 SENSOR MODULE 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // Connect sensor SDA and SCL pins to those of the ESP. 7 | 8 | #include 9 | #include "fdrs_sensor.h" 10 | 11 | Adafruit_BMP280 bmp; 12 | 13 | void setup() { 14 | //Serial.begin(115200); 15 | beginFDRS(); 16 | while (!bmp.begin(0x76)) { 17 | //Serial.println("BMP not initializing!"); 18 | delay(10); 19 | } 20 | } 21 | 22 | void loop() { 23 | loadFDRS(bmp.readTemperature(), TEMP_T); 24 | loadFDRS(bmp.readPressure() / 100.0F, PRESSURE_T); 25 | sendFDRS(); 26 | sleepFDRS(60); //Sleep time in seconds 27 | } 28 | -------------------------------------------------------------------------------- /Sensors/BMP280_fdrs/fdrs_sensor.h: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // "fdrs_sensor.h" 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // 7 | #define READING_ID 1 //Unique ID for sensor module 8 | #define GTWY_MAC 0x00 //Address of the nearest gateway 9 | 10 | #define POWER_CTRL 14 11 | #define MAC_PREFIX 0xAA, 0xBB, 0xCC, 0xDD, 0xEE 12 | //#define DEEP_SLEEP 13 | #define USE_ESPNOW 14 | 15 | //#define USE_LORA 16 | #define SCK 5 17 | #define MISO 19 18 | #define MOSI 27 19 | #define SS 18 20 | #define RST 14 21 | #define DIO0 26 22 | //433E6 for Asia 23 | //866E6 for Europe 24 | //915E6 for North America 25 | #define BAND 915E6 26 | 27 | typedef struct __attribute__((packed)) DataReading { 28 | float d; 29 | uint16_t id; 30 | uint8_t t; 31 | 32 | } DataReading; 33 | 34 | #define STATUS_T 0 // Status 35 | #define TEMP_T 1 // Temperature 36 | #define TEMP2_T 2 // Temperature #2 37 | #define HUMIDITY_T 3 // Relative Humidity 38 | #define PRESSURE_T 4 // Atmospheric Pressure 39 | #define LIGHT_T 5 // Light (lux) 40 | #define SOIL_T 6 // Soil Moisture 41 | #define SOIL2_T 7 // Soil Moisture #2 42 | #define SOILR_T 8 // Soil Resistance 43 | #define SOILR2_T 9 // Soil Resistance #2 44 | #define OXYGEN_T 10 // Oxygen 45 | #define CO2_T 11 // Carbon Dioxide 46 | #define WINDSPD_T 12 // Wind Speed 47 | #define WINDHDG_T 13 // Wind Direction 48 | #define RAINFALL_T 14 // Rainfall 49 | #define MOTION_T 15 // Motion 50 | #define VOLTAGE_T 16 // Voltage 51 | #define VOLTAGE2_T 17 // Voltage #2 52 | #define CURRENT_T 18 // Current 53 | #define CURRENT2_T 19 // Current #2 54 | #define IT_T 20 // Iterations 55 | 56 | 57 | #if defined(ESP8266) 58 | #include 59 | #include 60 | #elif defined(ESP32) 61 | #include 62 | #include 63 | #include 64 | #endif 65 | #include 66 | 67 | 68 | const uint16_t espnow_size = 250 / sizeof(DataReading); 69 | uint8_t gatewayAddress[] = {MAC_PREFIX, GTWY_MAC}; 70 | uint8_t gtwyAddress[] = {gatewayAddress[3], gatewayAddress[4], GTWY_MAC}; 71 | uint8_t LoRaAddress[] = {0x42, 0x00}; 72 | 73 | 74 | uint32_t wait_time = 0; 75 | DataReading fdrsData[espnow_size]; 76 | uint8_t data_count = 0; 77 | 78 | void beginFDRS() { 79 | #ifdef USE_ESPNOW 80 | WiFi.mode(WIFI_STA); 81 | WiFi.disconnect(); 82 | #ifdef POWER_CTRL 83 | pinMode(POWER_CTRL, OUTPUT); 84 | digitalWrite(POWER_CTRL, 1); 85 | #endif 86 | // Init ESP-NOW for either ESP8266 or ESP32 and set MAC address 87 | #if defined(ESP8266) 88 | if (esp_now_init() != 0) { 89 | return; 90 | } 91 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 92 | // Register peers 93 | esp_now_add_peer(gatewayAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 94 | #elif defined(ESP32) 95 | if (esp_now_init() != ESP_OK) { 96 | Serial.println("Error initializing ESP-NOW"); 97 | return; 98 | } 99 | esp_now_peer_info_t peerInfo; 100 | peerInfo.ifidx = WIFI_IF_STA; 101 | peerInfo.channel = 0; 102 | peerInfo.encrypt = false; 103 | // Register first peer 104 | memcpy(peerInfo.peer_addr, gatewayAddress, 6); 105 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 106 | Serial.println("Failed to add peer"); 107 | return; 108 | } 109 | #endif 110 | #endif 111 | #ifdef USE_LORA 112 | #ifndef __AVR__ 113 | SPI.begin(SCK, MISO, MOSI, SS); 114 | #endif 115 | LoRa.setPins(SS, RST, DIO0); 116 | if (!LoRa.begin(BAND)) { 117 | while (1); 118 | } 119 | #endif 120 | } 121 | void transmitLoRa(uint8_t* mac, DataReading * packet, uint8_t len) { 122 | #ifdef USE_LORA 123 | uint8_t pkt[5 + (len * sizeof(DataReading))]; 124 | memcpy(&pkt, mac, 3); 125 | memcpy(&pkt[3], &LoRaAddress, 2); 126 | memcpy(&pkt[5], packet, len * sizeof(DataReading)); 127 | LoRa.beginPacket(); 128 | LoRa.write((uint8_t*)&pkt, sizeof(pkt)); 129 | LoRa.endPacket(); 130 | #endif 131 | } 132 | void sendFDRS() { 133 | #ifdef USE_ESPNOW 134 | esp_now_send(gatewayAddress, (uint8_t *) &fdrsData, data_count * sizeof(DataReading)); 135 | delay(5); 136 | #endif 137 | #ifdef USE_LORA 138 | transmitLoRa(gtwyAddress, fdrsData, data_count); 139 | #endif 140 | data_count = 0; 141 | } 142 | void loadFDRS(float d, uint8_t t) { 143 | if (data_count > espnow_size) sendFDRS(); 144 | DataReading dr; 145 | dr.id = READING_ID; 146 | dr.t = t; 147 | dr.d = d; 148 | fdrsData[data_count] = dr; 149 | data_count++; 150 | 151 | } 152 | void sleepFDRS(int sleep_time) { 153 | #ifdef DEEP_SLEEP 154 | #ifdef ESP32 155 | esp_sleep_enable_timer_wakeup(sleep_time * 1000000); 156 | esp_deep_sleep_start(); 157 | #endif 158 | #ifdef ESP8266 159 | ESP.deepSleep(sleep_time * 1000000); 160 | #endif 161 | #endif 162 | delay(sleep_time * 1000); 163 | 164 | } 165 | -------------------------------------------------------------------------------- /Sensors/DHT22_8266/DHT22_8266.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // DHT SENSOR MODULE 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // Each reading is assigned a two-byte identifier along with a one-byte sensor type 7 | // 8 | 9 | #define READING_ID 1 //Unique integer for each data reading 10 | #define SLEEPYTIME 30 //Time to sleep in seconds 11 | #define GTWY_MAC 0x00 //Gateway MAC 12 | #define DHT_PIN 4 13 | 14 | 15 | #include 16 | #include 17 | #include "DHTesp.h" 18 | #include "DataReading.h" 19 | 20 | 21 | DHTesp dht; 22 | uint8_t broadcastAddress[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, GTWY_MAC}; 23 | int wait_time = 0; 24 | 25 | void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { 26 | Serial.print("Last Packet Send Status: "); 27 | if (sendStatus == 0) { 28 | Serial.println("Delivery success"); 29 | } 30 | else { 31 | Serial.println("Delivery fail"); 32 | } 33 | } 34 | 35 | 36 | void setup() { 37 | Serial.begin(115200); 38 | dht.setup(DHT_PIN, DHTesp::DHT22); 39 | Serial.println("DHT Initialized"); 40 | WiFi.mode(WIFI_STA); 41 | 42 | if (esp_now_init() != 0) { 43 | return; 44 | } 45 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 46 | esp_now_register_send_cb(OnDataSent); 47 | // Register peer 48 | esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 49 | } 50 | 51 | void loop() { 52 | if (millis() > wait_time) { 53 | wait_time = wait_time + SLEEPYTIME * 1000; 54 | loadData(); 55 | } 56 | } 57 | void loadData() { 58 | float h = dht.getHumidity(); 59 | float t = dht.getTemperature(); 60 | DataReading Temp; 61 | Temp.id = READING_ID; 62 | Temp.t = 1; 63 | DataReading Hum; 64 | Temp.id = READING_ID; 65 | Temp.t = 2; 66 | if (!(isnan(h) || isnan(t))) { 67 | Temp.d = -69.00; 68 | Hum.d = -4.20; 69 | } else { 70 | Temp.d = t; 71 | Hum.d = h; 72 | } 73 | DataPacket thePacket; 74 | thePacket.packet[0] = Temp; 75 | thePacket.packet[1] = Hum; 76 | 77 | esp_now_send(broadcastAddress, (uint8_t *) &thePacket, sizeof(thePacket)); 78 | 79 | } 80 | -------------------------------------------------------------------------------- /Sensors/DHT22_8266/DataReading.h: -------------------------------------------------------------------------------- 1 | 2 | typedef struct DataReading { 3 | float d; 4 | uint16_t id; 5 | uint8_t t; 6 | 7 | } DataReading; 8 | -------------------------------------------------------------------------------- /Sensors/DS18B20_8266/DS18B20_8266.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // DS18B20 SENSOR MODULE 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // Each reading is assigned a two-byte identifier along with a one-byte sensor type 7 | // 8 | 9 | #define READING_ID 29 //Unique integer for each data reading 10 | #define GTWY_MAC 0x00 //Gateway MAC 11 | #define SLEEPYTIME 30 //Time to sleep in seconds 12 | #define ONE_WIRE_BUS 13 //Pin that the DS18B20 is connected to 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include "DataReading.h" 19 | 20 | uint8_t broadcastAddress[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, GTWY_MAC}; 21 | int wait_time = 0; 22 | OneWire oneWire(ONE_WIRE_BUS); 23 | DallasTemperature sensors(&oneWire); 24 | 25 | void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { 26 | Serial.print("Last Packet Send Status: "); 27 | if (sendStatus == 0) { 28 | Serial.println("Delivery success"); 29 | } 30 | else { 31 | Serial.println("Delivery fail"); 32 | } 33 | } 34 | 35 | void setup() { 36 | Serial.begin(115200); 37 | WiFi.mode(WIFI_STA); 38 | if (esp_now_init() != 0) { 39 | return; 40 | } 41 | 42 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 43 | esp_now_register_send_cb(OnDataSent); 44 | // Register peer 45 | esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 46 | 47 | sensors.begin(); 48 | 49 | } 50 | 51 | void loop() { 52 | if (millis() > wait_time) { 53 | wait_time = wait_time + SLEEPYTIME * 1000; 54 | loadData(); 55 | } 56 | } 57 | void loadData() { 58 | sensors.requestTemperatures(); // Send the command to get temperatures 59 | float tempC = sensors.getTempCByIndex(0); 60 | DataReading Temp; 61 | Temp.id = READING_ID; 62 | Temp.t = 1; 63 | // if (tempC = DEVICE_DISCONNECTED_C) Temp.d = -69.00; 64 | // else Temp.d = tempC; 65 | Temp.d = tempC; 66 | esp_now_send(broadcastAddress, (uint8_t *) &Temp, sizeof(Temp)); 67 | 68 | } 69 | -------------------------------------------------------------------------------- /Sensors/DS18B20_8266/DataReading.h: -------------------------------------------------------------------------------- 1 | 2 | typedef struct DataReading { 3 | float d; 4 | uint16_t id; 5 | uint8_t t; 6 | 7 | } DataReading; 8 | -------------------------------------------------------------------------------- /Sensors/LilyGo_HiGrow_32/DataReading.h: -------------------------------------------------------------------------------- 1 | 2 | typedef struct DataReading { 3 | float d; 4 | uint16_t id; 5 | uint8_t t; 6 | 7 | } DataReading; 8 | -------------------------------------------------------------------------------- /Sensors/LilyGo_HiGrow_32/LilyGo_HiGrow_32.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // LILYGO HIGROW SENSOR MODULE 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // Each reading is assigned a two-byte identifier along with a one-byte sensor type 7 | 8 | 9 | #define READING_ID 18 //Unique ID (0 - 1023) for each data reading 10 | #define SLEEPYTIME 60 //Time to sleep in seconds 11 | #define GTWY_MAC 0x00 //Gateway MAC 12 | 13 | 14 | #define I2C_SDA 25 15 | #define I2C_SCL 26 16 | #define DHT12_PIN 16 17 | #define BAT_ADC 33 18 | #define SALT_PIN 34 19 | #define SOIL_PIN 32 20 | #define BOOT_PIN 0 21 | #define POWER_CTRL 4 22 | #define USER_BUTTON 35 23 | #define DS18B20_PIN 21 24 | 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "DataReading.h" 30 | 31 | BH1750 lightMeter(0x23); //0x23 32 | Adafruit_BME280 bmp; //0x77 33 | RTC_DATA_ATTR int the_count = 0; 34 | uint8_t broadcastAddress[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, GTWY_MAC}; 35 | 36 | void OnDataSent(const uint8_t *mac_addr, esp_now_send_status_t status) { 37 | // Serial.print("Last Packet Send Status:"); 38 | // Serial.println(status == ESP_NOW_SEND_SUCCESS ? "Delivery Success" : "Delivery Fail"); 39 | esp_deep_sleep_start(); 40 | } 41 | 42 | void setup() { 43 | //Serial.begin(115200); 44 | // Sensor power control pin, set high to enable sensors. 45 | pinMode(POWER_CTRL, OUTPUT); 46 | digitalWrite(POWER_CTRL, 1); 47 | //Init Sensors 48 | Wire.begin(I2C_SDA, I2C_SCL); 49 | while (!bmp.begin()) { 50 | //Serial.println("bmp"); 51 | delay(10); 52 | } 53 | 54 | if (lightMeter.begin(BH1750::CONTINUOUS_HIGH_RES_MODE)) { 55 | Serial.println(F("BH1750 Advanced begin")); 56 | } else { 57 | Serial.println(F("Error initialising BH1750")); 58 | } 59 | 60 | WiFi.mode(WIFI_STA); 61 | WiFi.disconnect(); 62 | 63 | // Init ESP-NOW 64 | if (esp_now_init() != ESP_OK) { 65 | Serial.println("Error initializing ESP-NOW"); 66 | return; 67 | } 68 | esp_now_register_send_cb(OnDataSent); 69 | esp_now_peer_info_t peerInfo; 70 | memcpy(peerInfo.peer_addr, broadcastAddress, 6); 71 | peerInfo.channel = 0; 72 | peerInfo.encrypt = false; 73 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 74 | Serial.println("Failed to add peer"); 75 | return; 76 | } 77 | loadData(); 78 | digitalWrite(POWER_CTRL, 0); 79 | esp_sleep_enable_timer_wakeup(SLEEPYTIME * 1000000); 80 | 81 | } 82 | 83 | void loadData() { 84 | float s_battery = readBattery(); 85 | float bme_temp = bmp.readTemperature(); 86 | float bme_pressure = (bmp.readPressure() / 100.0F); 87 | //float bme_altitude = bmp.readAltitude(1013.25); 88 | float bme_humidity = bmp.readHumidity(); 89 | float s_soil = readSoil(); 90 | float s_salt = readSalt(); 91 | while (! lightMeter.measurementReady()) { 92 | delay(10); 93 | } 94 | float lux = lightMeter.readLightLevel(); 95 | the_count++; 96 | 97 | Serial.println(); 98 | Serial.println("Temp:" + String(bme_temp)); 99 | Serial.println("Humidity:" + String(bme_humidity)); 100 | Serial.println("Light:" + String(lux)); 101 | Serial.println("Pressure:" + String(bme_pressure)); 102 | Serial.println("Salt:" + String(s_salt)); 103 | Serial.println("Soil:" + String(s_soil)); 104 | Serial.println("Voltage:" + String(s_battery)); 105 | Serial.println("Count:" + String(the_count)); 106 | 107 | DataReading Temp; 108 | Temp.d = bme_temp; 109 | Temp.id = READING_ID; 110 | Temp.t = 1; 111 | 112 | DataReading Hum; 113 | Hum.d = bme_humidity; 114 | Hum.id = READING_ID; 115 | Hum.t = 2; 116 | 117 | DataReading Lux; 118 | Lux.d = lux; 119 | Lux.id = READING_ID; 120 | Lux.t = 4; 121 | 122 | DataReading Pres; 123 | Pres.d = bme_pressure; 124 | Pres.id = READING_ID; 125 | Pres.t = 3; 126 | 127 | DataReading Salt; 128 | Salt.d = s_salt; 129 | Salt.id = READING_ID; 130 | Salt.t = 6; 131 | 132 | DataReading Soil; 133 | Soil.d = s_soil; 134 | Soil.id = READING_ID; 135 | Soil.t = 5; 136 | 137 | DataReading Bat; 138 | Bat.d = s_battery; 139 | Bat.id = READING_ID; 140 | Bat.t = 50; 141 | 142 | DataReading Count; 143 | Count.d = float(the_count); 144 | Count.id = READING_ID; 145 | Count.t = 51; 146 | 147 | DataReading thePacket[8]; 148 | thePacket[0] = Temp; 149 | thePacket[1] = Hum; 150 | thePacket[2] = Lux; 151 | thePacket[3] = Pres; 152 | thePacket[4] = Salt; 153 | thePacket[5] = Soil; 154 | thePacket[6] = Bat; 155 | thePacket[7] = Count; 156 | //Serial.println(sizeof(thePacket), DEC); 157 | 158 | esp_now_send(broadcastAddress, (uint8_t *) &thePacket, sizeof(thePacket)); 159 | } 160 | 161 | uint32_t readSalt() //Soil Electrodes: This code came from the LilyGo documentation. 162 | { 163 | uint8_t samples = 120; 164 | uint32_t humi = 0; 165 | uint16_t array[120]; 166 | for (int i = 0; i < samples; i++) { 167 | array[i] = analogRead(SALT_PIN); 168 | delay(2); 169 | } 170 | std::sort(array, array + samples); 171 | for (int i = 0; i < samples; i++) { 172 | if (i == 0 || i == samples - 1)continue; 173 | humi += array[i]; 174 | } 175 | humi /= samples - 2; 176 | return humi; 177 | } 178 | 179 | uint16_t readSoil() //Soil Capacitance: This code came from the LilyGo documentation. 180 | { 181 | uint16_t soil = analogRead(SOIL_PIN); 182 | return map(soil, 0, 4095, 100, 0); 183 | } 184 | 185 | float readBattery() //Battery Voltage: This code came from the LilyGo documentation. 186 | { 187 | int vref = 1100; 188 | uint16_t volt = analogRead(BAT_ADC); 189 | float battery_voltage = ((float)volt / 4095.0) * 2.0 * 3.3 * (vref); 190 | return battery_voltage; 191 | } 192 | void loop() { 193 | } 194 | -------------------------------------------------------------------------------- /Sensors/MESB_fdrs/MESB_fdrs.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // Multifunction ESP8266 Sensor Board by Phil Grant 4 | // 5 | // https://github.com/gadjet/Multifunction-ESP8266-Sensor-board 6 | // 7 | // 8 | #include 9 | #include "fdrs_sensor.h" 10 | 11 | Adafruit_AHT10 aht; 12 | 13 | const int reedSwitch = 13; 14 | 15 | void setup() { 16 | aht.begin(); 17 | 18 | // Init Serial Monitor 19 | //Serial.begin(115200); 20 | // initialize the reed switch pin as an input: 21 | pinMode(reedSwitch, INPUT); 22 | // initialize the wakeup pin as an input: 23 | pinMode(16, WAKEUP_PULLUP); 24 | beginFDRS(); 25 | } 26 | 27 | 28 | void loop() { 29 | sensors_event_t humidity, temp; 30 | aht.getEvent(&humidity, &temp);// populate temp and humidity objects with fresh data 31 | // Read the state of the reed switch and send open or closed 32 | if (digitalRead(reedSwitch) == HIGH) { 33 | loadFDRS(1.0, MOTION_T); 34 | } 35 | else { 36 | loadFDRS(0.0, MOTION_T); 37 | } 38 | loadFDRS((analogRead(A0) * 4.2 * 10 / 1023), VOLTAGE_T); 39 | loadFDRS(humidity.relative_humidity, HUMIDITY_T); 40 | loadFDRS(temp.temperature, TEMP_T); 41 | // Send message via FDRS 42 | sendFDRS(); 43 | sleepFDRS(15); //15 Min's sleep 44 | } 45 | -------------------------------------------------------------------------------- /Sensors/MESB_fdrs/fdrs_sensor.h: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // "fdrs_sensor.h" 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // 7 | #define READING_ID 43 //Unique ID for sensor module 8 | #define GTWY_MAC 0x00 //Address of the nearest gateway 9 | 10 | //#define POWER_CTRL 14 11 | #define MAC_PREFIX 0xAA, 0xBB, 0xCC, 0xDD, 0xEE 12 | #define DEEP_SLEEP 13 | #define USE_ESPNOW 14 | 15 | //#define USE_LORA 16 | #define SCK 5 17 | #define MISO 19 18 | #define MOSI 27 19 | #define SS 18 20 | #define RST 14 21 | #define DIO0 26 22 | //433E6 for Asia 23 | //866E6 for Europe 24 | //915E6 for North America 25 | #define BAND 866E6 26 | 27 | typedef struct __attribute__((packed)) DataReading { 28 | float d; 29 | uint16_t id; 30 | uint8_t t; 31 | 32 | } DataReading; 33 | 34 | #define STATUS_T 0 // Status 35 | #define TEMP_T 1 // Temperature 36 | #define TEMP2_T 2 // Temperature #2 37 | #define HUMIDITY_T 3 // Relative Humidity 38 | #define PRESSURE_T 4 // Atmospheric Pressure 39 | #define LIGHT_T 5 // Light (lux) 40 | #define SOIL_T 6 // Soil Moisture 41 | #define SOIL2_T 7 // Soil Moisture #2 42 | #define SOILR_T 8 // Soil Resistance 43 | #define SOILR2_T 9 // Soil Resistance #2 44 | #define OXYGEN_T 10 // Oxygen 45 | #define CO2_T 11 // Carbon Dioxide 46 | #define WINDSPD_T 12 // Wind Speed 47 | #define WINDHDG_T 13 // Wind Direction 48 | #define RAINFALL_T 14 // Rainfall 49 | #define MOTION_T 15 // Motion 50 | #define VOLTAGE_T 16 // Voltage 51 | #define VOLTAGE2_T 17 // Voltage #2 52 | #define CURRENT_T 18 // Current 53 | #define CURRENT2_T 19 // Current #2 54 | #define IT_T 20 // Iterations 55 | 56 | 57 | #if defined(ESP8266) 58 | #include 59 | #include 60 | #elif defined(ESP32) 61 | #include 62 | #include 63 | #include 64 | #endif 65 | #include 66 | 67 | 68 | const uint16_t espnow_size = 250 / sizeof(DataReading); 69 | uint8_t gatewayAddress[] = {MAC_PREFIX, GTWY_MAC}; 70 | uint8_t gtwyAddress[] = {gatewayAddress[3], gatewayAddress[4], GTWY_MAC}; 71 | uint8_t LoRaAddress[] = {0x42, 0x00}; 72 | 73 | 74 | uint32_t wait_time = 0; 75 | DataReading fdrsData[espnow_size]; 76 | uint8_t data_count = 0; 77 | 78 | void beginFDRS() { 79 | #ifdef USE_ESPNOW 80 | WiFi.mode(WIFI_STA); 81 | WiFi.disconnect(); 82 | #ifdef POWER_CTRL 83 | pinMode(POWER_CTRL, OUTPUT); 84 | digitalWrite(POWER_CTRL, 1); 85 | #endif 86 | // Init ESP-NOW for either ESP8266 or ESP32 and set MAC address 87 | #if defined(ESP8266) 88 | if (esp_now_init() != 0) { 89 | return; 90 | } 91 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 92 | // Register peers 93 | esp_now_add_peer(gatewayAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 94 | #elif defined(ESP32) 95 | if (esp_now_init() != ESP_OK) { 96 | Serial.println("Error initializing ESP-NOW"); 97 | return; 98 | } 99 | esp_now_peer_info_t peerInfo; 100 | peerInfo.ifidx = WIFI_IF_STA; 101 | peerInfo.channel = 0; 102 | peerInfo.encrypt = false; 103 | // Register first peer 104 | memcpy(peerInfo.peer_addr, gatewayAddress, 6); 105 | if (esp_now_add_peer(&peerInfo) != ESP_OK) { 106 | Serial.println("Failed to add peer"); 107 | return; 108 | } 109 | #endif 110 | #endif 111 | #ifdef USE_LORA 112 | #ifndef __AVR__ 113 | SPI.begin(SCK, MISO, MOSI, SS); 114 | #endif 115 | LoRa.setPins(SS, RST, DIO0); 116 | if (!LoRa.begin(BAND)) { 117 | while (1); 118 | } 119 | #endif 120 | } 121 | void transmitLoRa(uint8_t* mac, DataReading * packet, uint8_t len) { 122 | #ifdef USE_LORA 123 | uint8_t pkt[5 + (len * sizeof(DataReading))]; 124 | memcpy(&pkt, mac, 3); 125 | memcpy(&pkt[3], &LoRaAddress, 2); 126 | memcpy(&pkt[5], packet, len * sizeof(DataReading)); 127 | LoRa.beginPacket(); 128 | LoRa.write((uint8_t*)&pkt, sizeof(pkt)); 129 | LoRa.endPacket(); 130 | #endif 131 | } 132 | void sendFDRS() { 133 | #ifdef USE_ESPNOW 134 | esp_now_send(gatewayAddress, (uint8_t *) &fdrsData, data_count * sizeof(DataReading)); 135 | delay(5); 136 | #endif 137 | #ifdef USE_LORA 138 | transmitLoRa(gtwyAddress, fdrsData, data_count); 139 | #endif 140 | data_count = 0; 141 | } 142 | void loadFDRS(float d, uint8_t t) { 143 | if (data_count > espnow_size) sendFDRS(); 144 | DataReading dr; 145 | dr.id = READING_ID; 146 | dr.t = t; 147 | dr.d = d; 148 | fdrsData[data_count] = dr; 149 | data_count++; 150 | 151 | } 152 | void sleepFDRS(int sleep_time) { 153 | #ifdef DEEP_SLEEP 154 | #ifdef ESP32 155 | esp_sleep_enable_timer_wakeup(sleep_time * 1000000); 156 | esp_deep_sleep_start(); 157 | #endif 158 | #ifdef ESP8266 159 | ESP.deepSleep(sleep_time * 1000000); 160 | #endif 161 | #endif 162 | delay(sleep_time * 1000); 163 | 164 | } 165 | -------------------------------------------------------------------------------- /Sensors/MotionDetector/DataReading.h: -------------------------------------------------------------------------------- 1 | 2 | typedef struct DataReading { 3 | float d; 4 | uint16_t id; 5 | uint8_t t; 6 | 7 | } DataReading; 8 | -------------------------------------------------------------------------------- /Sensors/MotionDetector/MotionDetector.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // MOTION SENSOR MODULE 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // Each reading is assigned a two-byte identifier along with a one-byte sensor type 7 | // This code is unfinished, but may work. 8 | // 9 | #define READING_ID 32 //Unique integer for each data reading 10 | #define GTWY_MAC 0x00 //Terminal MAC 11 | 12 | #define MOTION_PIN 13 13 | #include 14 | #include 15 | #include "DataReading.h" 16 | unsigned int theCount = 0; 17 | unsigned long nextCheck = 0; 18 | boolean motionDetected = false; 19 | unsigned long isr_time = millis(); 20 | 21 | uint8_t broadcastAddress[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, GTWY_MAC}; 22 | 23 | 24 | ICACHE_RAM_ATTR void detectsMovement() { 25 | if (millis() > isr_time + 15000) { 26 | nextCheck = millis(); 27 | isr_time = millis(); 28 | } 29 | } 30 | 31 | 32 | void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { 33 | Serial.print("Last Packet Send Status: "); 34 | if (sendStatus == 0) { 35 | Serial.println("Delivery success"); 36 | } 37 | else { 38 | Serial.println("Delivery fail"); 39 | } 40 | } 41 | // Checks if motion was detected, sets LED HIGH and starts a timer 42 | 43 | void setup() { 44 | // Serial port for debugging purposes 45 | Serial.begin(115200); 46 | WiFi.mode(WIFI_STA); 47 | if (esp_now_init() != 0) { 48 | Serial.println("Error initializing ESP-NOW"); 49 | return; 50 | } 51 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 52 | esp_now_register_send_cb(OnDataSent); 53 | // Register peer 54 | esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 55 | 56 | pinMode(MOTION_PIN, INPUT); 57 | attachInterrupt(digitalPinToInterrupt(MOTION_PIN), detectsMovement, RISING); 58 | 59 | 60 | } 61 | 62 | void loop() { 63 | if ( millis() > nextCheck) { 64 | checkDetector(); 65 | 66 | } 67 | } 68 | void checkDetector() { 69 | 70 | Serial.println("Checking"); 71 | 72 | nextCheck = millis() + 15000; 73 | motionDetected = digitalRead(MOTION_PIN); 74 | if (motionDetected == HIGH) { 75 | Serial.println("Motion Detected"); 76 | 77 | DataReading Motion; 78 | Motion.d = theCount; 79 | Motion.id = READING_ID; 80 | Motion.t = 8; 81 | esp_now_send(broadcastAddress, (uint8_t *) &Motion, sizeof(Motion)); 82 | } else { 83 | 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /Sensors/README.md: -------------------------------------------------------------------------------- 1 | # FDRS Sensors 2 | I would like to use this spot as a showcase for open source PCB designs. If **you** have designed a sensor module that runs on either ESP8266 or ESP32, please contact me at timmbogner@gmail.com. I will make a custom sensor sketch, along with a link and description available here. 3 | -------------------------------------------------------------------------------- /Sensors/TippingBucket/DataReading.h: -------------------------------------------------------------------------------- 1 | 2 | typedef struct DataReading { 3 | float d; 4 | uint16_t id; 5 | uint8_t t; 6 | 7 | } DataReading; 8 | -------------------------------------------------------------------------------- /Sensors/TippingBucket/TippingBucket.ino: -------------------------------------------------------------------------------- 1 | // FARM DATA RELAY SYSTEM 2 | // 3 | // TIPPING BUCKET RAINFALL SENSOR MODULE 4 | // 5 | // Developed by Timm Bogner (bogner1@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA. 6 | // Each reading is assigned a two-byte identifier along with a one-byte sensor type 7 | // 8 | 9 | #define READING_ID 45 //Unique integer for each data reading 10 | #define GTWY_MAC 0x00 //Gateway MAC 11 | 12 | #define REED_PIN 2 13 | #include 14 | #include 15 | #include "DataReading.h" 16 | unsigned int theCount = 0; 17 | unsigned long lastTrigger = 0; 18 | boolean clicked = false; 19 | 20 | uint8_t broadcastAddress[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, GTWY_MAC}; 21 | 22 | void OnDataSent(uint8_t *mac_addr, uint8_t sendStatus) { 23 | Serial.print("Last Packet Send Status: "); 24 | if (sendStatus == 0) { 25 | Serial.println("Delivery success"); 26 | } 27 | else { 28 | Serial.println("Delivery fail"); 29 | } 30 | } 31 | // Checks if motion was detected, sets LED HIGH and starts a timer 32 | ICACHE_RAM_ATTR void detectsMovement() { 33 | clicked = true; 34 | lastTrigger = millis(); 35 | } 36 | 37 | void setup() { 38 | // Serial port for debugging purposes 39 | Serial.begin(115200); 40 | WiFi.mode(WIFI_STA); 41 | if (esp_now_init() != 0) { 42 | Serial.println("Error initializing ESP-NOW"); 43 | return; 44 | } 45 | esp_now_set_self_role(ESP_NOW_ROLE_COMBO); 46 | esp_now_register_send_cb(OnDataSent); 47 | // Register peer 48 | esp_now_add_peer(broadcastAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0); 49 | 50 | pinMode(REED_PIN, INPUT_PULLUP); 51 | attachInterrupt(digitalPinToInterrupt(REED_PIN), detectsMovement, FALLING); 52 | } 53 | 54 | void loop() { 55 | if (clicked && millis() - lastTrigger > 100) { 56 | theCount++; 57 | Serial.print("CLICK."); 58 | Serial.println(theCount); 59 | clicked = false; 60 | DataReading Rain; 61 | Rain.d = theCount; 62 | Rain.id = READING_ID; 63 | Rain.t = 7; 64 | esp_now_send(broadcastAddress, (uint8_t *) &Rain, sizeof(Rain)); 65 | } 66 | 67 | } 68 | --------------------------------------------------------------------------------