mirror of
https://github.com/timmbogner/Farm-Data-Relay-System
synced 2024-11-10 07:10:42 +00:00
Added GeneralGPS sensor
Added GPS sensor, which will transmit longitude, latitude and altitude. I also added these datatypes to the definition list. I'm not quite sure if that was the right place to do so - feel free to correct. :)
This commit is contained in:
parent
3818d88dd5
commit
4257a60422
168
Sensors/GenericGPS_fdrs/GenericGPS_fdrs.ino
Normal file
168
Sensors/GenericGPS_fdrs/GenericGPS_fdrs.ino
Normal file
@ -0,0 +1,168 @@
|
||||
// FARM DATA RELAY SYSTEM
|
||||
//
|
||||
// Generic GPS Sensor
|
||||
//
|
||||
// Developed by Sascha Juch (sascha.juch@gmail.com).
|
||||
// Reads in GPS data from serial and sends latitude, longitude and altitude to a gateway.
|
||||
//
|
||||
|
||||
#include "fdrs_sensor.h"
|
||||
|
||||
#define SERIAL1_RX 34 // TX pin of GPS sensor
|
||||
#define SERIAL1_TX 12 // RX pin of GPS sensor
|
||||
#define MAX_NMEA_LENGTH 82 //maximum allowed length of a NMEA 0183 sentences.
|
||||
|
||||
char currentNMEALine[MAX_NMEA_LENGTH];
|
||||
|
||||
void setup() {
|
||||
// ToDo: This works well on a board with a second hardware serial port like the ESP32. But what if there is no hardware serial on the device?
|
||||
// Unfortunately I do not have a GPS (standalone) sensor atm with which I could test. Help and advice appreciated.
|
||||
Serial1.begin(9600, SERIAL_8N1, SERIAL1_RX, SERIAL1_TX);
|
||||
|
||||
beginFDRS();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// read in line by line of the NMEA input and get rid of trailing whitespaces
|
||||
Serial1.readBytesUntil('\n', currentNMEALine, MAX_NMEA_LENGTH);
|
||||
trimwhitespace(currentNMEALine);
|
||||
|
||||
// we are only interested in GPGGA (U-Blox M6N) or GNGGA (U-Blox M8N)lines.
|
||||
if (startsWith(currentNMEALine, "$GNGGA") || startsWith(currentNMEALine, "$GPGGA")) {
|
||||
|
||||
DBG(currentNMEALine);
|
||||
|
||||
// just in case someone needs UTC, quality or #satelites, just uncomment and do what you have to do with them. :)
|
||||
//char * gpsUTC = getNthValueOf(currentNMEALine, ',', 1);
|
||||
char * gpsLatitude = getNthValueOf(currentNMEALine, ',', 2);
|
||||
char * gpsLatitudeOrientation = getNthValueOf(currentNMEALine, ',', 3);
|
||||
char * gpsLongitude = getNthValueOf(currentNMEALine, ',', 4);
|
||||
char * gpsLongitudeOrientation = getNthValueOf(currentNMEALine, ',', 5);
|
||||
//char * gpsQuality = getNthValueOf(currentNMEALine, ',', 6);
|
||||
char * gpsAltitude = getNthValueOf(currentNMEALine, ',', 7);
|
||||
//char * gpsNoOfSatelites = getNthValueOf(currentNMEALine, ',', 9);
|
||||
|
||||
// convert latitude and altitude to decimal degree values (as used in most maps programs)
|
||||
// negative values mean "S" or "W", positive values mean "N" and "E"
|
||||
float latitude = convertGpsCoordinates(atof(gpsLatitude), gpsLatitudeOrientation);
|
||||
float longitude = convertGpsCoordinates(atof(gpsLongitude), gpsLongitudeOrientation);
|
||||
float altitude = atof(gpsAltitude);
|
||||
|
||||
/*
|
||||
loadFDRS(latitude, HUMIDITY_T);
|
||||
loadFDRS(longitude, TEMP_T);
|
||||
loadFDRS(altitude, TEMP2_T);
|
||||
*/
|
||||
|
||||
// extended sensor types - not officially atm!
|
||||
loadFDRS(latitude, LATITUDE_T);
|
||||
loadFDRS(longitude, LONGITUDE_T);
|
||||
loadFDRS(altitude, ALTITUDE_T);
|
||||
|
||||
|
||||
sendFDRS();
|
||||
sleepFDRS(10); //Sleep time in seconds
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// cudos for the trimming function go to: https://stackoverflow.com/questions/122616/how-do-i-trim-leading-trailing-whitespace-in-a-standard-way
|
||||
// Thanks! That was a time saver. :)
|
||||
// Note: This function returns a pointer to a substring of the original string.
|
||||
// If the given string was allocated dynamically, the caller must not overwrite
|
||||
// that pointer with the returned value, since the original pointer must be
|
||||
// deallocated using the same allocator with which it was allocated. The return
|
||||
// value must NOT be deallocated using free() etc.
|
||||
char *trimwhitespace(char *str)
|
||||
{
|
||||
char *end;
|
||||
|
||||
// Trim leading space
|
||||
while(isspace((unsigned char)*str)) str++;
|
||||
|
||||
if(*str == 0) // All spaces?
|
||||
return str;
|
||||
|
||||
// Trim trailing space
|
||||
end = str + strlen(str) - 1;
|
||||
while(end > str && isspace((unsigned char)*end)) end--;
|
||||
|
||||
// Write new null terminator character
|
||||
end[1] = '\0';
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
// check, if a given char* fullString starts with a given char* startString.
|
||||
// If that's the case, return true, false otherwise
|
||||
bool startsWith(const char *fullString, const char *startString)
|
||||
{
|
||||
if (strncmp(fullString, startString, strlen(startString)) == 0) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Cudos for the substr function go to: https://www.techiedelight.com/implement-substr-function-c/
|
||||
// Thanks! That helped a lot :)
|
||||
// Following function extracts characters present in `src`
|
||||
// between `m` and `n` (excluding `n`)
|
||||
char* substr(const char *src, int m, int n)
|
||||
{
|
||||
// get the length of the destination string
|
||||
int len = n - m;
|
||||
|
||||
// allocate (len + 1) chars for destination (+1 for extra null character)
|
||||
char *dest = (char*)malloc(sizeof(char) * (len + 1));
|
||||
|
||||
// extracts characters between m'th and n'th index from source string
|
||||
// and copy them into the destination string
|
||||
for (int i = m; i < n && (*(src + i) != '\0'); i++)
|
||||
{
|
||||
*dest = *(src + i);
|
||||
dest++;
|
||||
}
|
||||
|
||||
// null-terminate the destination string
|
||||
*dest = '\0';
|
||||
|
||||
// return the destination string
|
||||
return dest - len;
|
||||
}
|
||||
|
||||
// returns the value of the n-th occurance within a delimiter-separated string
|
||||
char * getNthValueOf (char *inputString, const char delimiter, uint8_t index) {
|
||||
uint8_t i = 0;
|
||||
uint8_t currentIndex = 0;
|
||||
uint8_t startOfValue = 0;
|
||||
uint8_t endOfValue = 0;
|
||||
|
||||
while (i < strlen(inputString) && inputString[i] && currentIndex < index) {
|
||||
if (inputString[i] == delimiter) {
|
||||
currentIndex++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
startOfValue = i;
|
||||
|
||||
while (i < strlen(inputString) && inputString[i] && currentIndex <= index) {
|
||||
if (inputString[i] == delimiter) {
|
||||
currentIndex++;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
endOfValue = i;
|
||||
|
||||
char* valueAtIndex = substr(inputString, startOfValue, endOfValue-1);
|
||||
|
||||
return valueAtIndex;
|
||||
}
|
||||
|
||||
// convert NMEA0183 degrees minutes coordinates to decimal degrees
|
||||
float convertGpsCoordinates(float degreesMinutes, char* orientation) {
|
||||
double gpsMinutes = fmod((double)degreesMinutes, 100.0);
|
||||
uint8_t gpsDegrees = degreesMinutes / 100;
|
||||
double decimalDegrees = gpsDegrees + ( gpsMinutes / 60 );
|
||||
if (orientation == "W" || orientation == "S") {
|
||||
decimalDegrees = 0 - decimalDegrees;
|
||||
}
|
||||
return decimalDegrees;
|
||||
}
|
181
Sensors/GenericGPS_fdrs/fdrs_sensor.h
Normal file
181
Sensors/GenericGPS_fdrs/fdrs_sensor.h
Normal file
@ -0,0 +1,181 @@
|
||||
// FARM DATA RELAY SYSTEM
|
||||
//
|
||||
// "fdrs_sensor.h"
|
||||
//
|
||||
// Developed by Timm Bogner (timmbogner@gmail.com) for Sola Gratia Farm in Urbana, Illinois, USA.
|
||||
//
|
||||
#include "sensor_setup.h"
|
||||
#if defined(ESP8266)
|
||||
#include <ESP8266WiFi.h>
|
||||
#include <espnow.h>
|
||||
#elif defined(ESP32)
|
||||
#include <esp_now.h>
|
||||
#include <WiFi.h>
|
||||
#include <esp_wifi.h>
|
||||
#endif
|
||||
#ifdef USE_LORA
|
||||
#include <LoRa.h>
|
||||
#endif
|
||||
|
||||
#ifdef GLOBALS
|
||||
#define FDRS_BAND GLOBAL_BAND
|
||||
#define FDRS_SF GLOBAL_SF
|
||||
#else
|
||||
#define FDRS_BAND BAND
|
||||
#define FDRS_SF SF
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG
|
||||
#define DBG(a) (Serial.println(a))
|
||||
#else
|
||||
#define DBG(a)
|
||||
#endif
|
||||
|
||||
#define STATUS_T 0 // Status
|
||||
#define TEMP_T 1 // Temperature
|
||||
#define TEMP2_T 2 // Temperature #2
|
||||
#define HUMIDITY_T 3 // Relative Humidity
|
||||
#define PRESSURE_T 4 // Atmospheric Pressure
|
||||
#define LIGHT_T 5 // Light (lux)
|
||||
#define SOIL_T 6 // Soil Moisture
|
||||
#define SOIL2_T 7 // Soil Moisture #2
|
||||
#define SOILR_T 8 // Soil Resistance
|
||||
#define SOILR2_T 9 // Soil Resistance #2
|
||||
#define OXYGEN_T 10 // Oxygen
|
||||
#define CO2_T 11 // Carbon Dioxide
|
||||
#define WINDSPD_T 12 // Wind Speed
|
||||
#define WINDHDG_T 13 // Wind Direction
|
||||
#define RAINFALL_T 14 // Rainfall
|
||||
#define MOTION_T 15 // Motion
|
||||
#define VOLTAGE_T 16 // Voltage
|
||||
#define VOLTAGE2_T 17 // Voltage #2
|
||||
#define CURRENT_T 18 // Current
|
||||
#define CURRENT2_T 19 // Current #2
|
||||
#define IT_T 20 // Iterations
|
||||
#define LATITUDE_T 21 // Latitude part of GPS coordinate
|
||||
#define LONGITUDE_T 22 // Longitude part of GPS coordinate
|
||||
#define ALTITUDE_T 23 // Altitude part of GPS coordinate
|
||||
|
||||
#define MAC_PREFIX 0xAA, 0xBB, 0xCC, 0xDD, 0xEE // Should only be changed if implementing multiple FDRS systems.
|
||||
|
||||
typedef struct __attribute__((packed)) DataReading {
|
||||
float d;
|
||||
uint16_t id;
|
||||
uint8_t t;
|
||||
|
||||
} DataReading;
|
||||
|
||||
const uint16_t espnow_size = 250 / sizeof(DataReading);
|
||||
uint8_t gatewayAddress[] = {MAC_PREFIX, GTWY_MAC};
|
||||
uint8_t gtwyAddress[] = {gatewayAddress[3], gatewayAddress[4], GTWY_MAC};
|
||||
uint8_t LoRaAddress[] = {0x42, 0x00};
|
||||
|
||||
uint32_t wait_time = 0;
|
||||
DataReading fdrsData[espnow_size];
|
||||
uint8_t data_count = 0;
|
||||
|
||||
void beginFDRS() {
|
||||
#ifdef DEBUG
|
||||
Serial.begin(115200);
|
||||
#endif
|
||||
DBG("FDRS Sensor ID " + String(READING_ID) + " initializing...");
|
||||
DBG(" Gateway: " + String (GTWY_MAC, HEX));
|
||||
#ifdef POWER_CTRL
|
||||
DBG("Powering up the sensor array!");
|
||||
pinMode(POWER_CTRL, OUTPUT);
|
||||
digitalWrite(POWER_CTRL, 1);
|
||||
#endif
|
||||
// Init ESP-NOW for either ESP8266 or ESP32 and set MAC address
|
||||
#ifdef USE_ESPNOW
|
||||
DBG("Initializing ESP-NOW!");
|
||||
WiFi.mode(WIFI_STA);
|
||||
WiFi.disconnect();
|
||||
#if defined(ESP8266)
|
||||
if (esp_now_init() != 0) {
|
||||
return;
|
||||
}
|
||||
esp_now_set_self_role(ESP_NOW_ROLE_COMBO);
|
||||
// Register peers
|
||||
esp_now_add_peer(gatewayAddress, ESP_NOW_ROLE_COMBO, 0, NULL, 0);
|
||||
#elif defined(ESP32)
|
||||
if (esp_now_init() != ESP_OK) {
|
||||
DBG("Error initializing ESP-NOW");
|
||||
return;
|
||||
}
|
||||
esp_now_peer_info_t peerInfo;
|
||||
peerInfo.ifidx = WIFI_IF_STA;
|
||||
peerInfo.channel = 0;
|
||||
peerInfo.encrypt = false;
|
||||
// Register first peer
|
||||
memcpy(peerInfo.peer_addr, gatewayAddress, 6);
|
||||
if (esp_now_add_peer(&peerInfo) != ESP_OK) {
|
||||
DBG("Failed to add peer");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
DBG(" ESP-NOW Initialized.");
|
||||
#endif
|
||||
#ifdef USE_LORA
|
||||
DBG("Initializing LoRa!");
|
||||
DBG(BAND);
|
||||
DBG(SF);
|
||||
#ifndef __AVR__
|
||||
SPI.begin(SCK, MISO, MOSI, SS);
|
||||
#endif
|
||||
LoRa.setPins(SS, RST, DIO0);
|
||||
if (!LoRa.begin(FDRS_BAND)) {
|
||||
while (1);
|
||||
}
|
||||
LoRa.setSpreadingFactor(FDRS_SF);
|
||||
DBG(" LoRa Initialized.");
|
||||
#endif
|
||||
}
|
||||
void transmitLoRa(uint8_t* mac, DataReading * packet, uint8_t len) {
|
||||
#ifdef USE_LORA
|
||||
uint8_t pkt[5 + (len * sizeof(DataReading))];
|
||||
memcpy(&pkt, mac, 3); //
|
||||
memcpy(&pkt[3], &LoRaAddress, 2);
|
||||
memcpy(&pkt[5], packet, len * sizeof(DataReading));
|
||||
LoRa.beginPacket();
|
||||
LoRa.write((uint8_t*)&pkt, sizeof(pkt));
|
||||
LoRa.endPacket();
|
||||
#endif
|
||||
}
|
||||
void sendFDRS() {
|
||||
DBG("Sending FDRS Packet!");
|
||||
#ifdef USE_ESPNOW
|
||||
esp_now_send(gatewayAddress, (uint8_t *) &fdrsData, data_count * sizeof(DataReading));
|
||||
delay(5);
|
||||
DBG(" ESP-NOW sent.");
|
||||
#endif
|
||||
#ifdef USE_LORA
|
||||
transmitLoRa(gtwyAddress, fdrsData, data_count);
|
||||
DBG(" LoRa sent.");
|
||||
#endif
|
||||
data_count = 0;
|
||||
}
|
||||
void loadFDRS(float d, uint8_t t) {
|
||||
DBG("Data loaded. Type: " + String(t));
|
||||
if (data_count > espnow_size) sendFDRS();
|
||||
DataReading dr;
|
||||
dr.id = READING_ID;
|
||||
dr.t = t;
|
||||
dr.d = d;
|
||||
fdrsData[data_count] = dr;
|
||||
data_count++;
|
||||
}
|
||||
void sleepFDRS(int sleep_time) {
|
||||
DBG("Sleepytime!");
|
||||
#ifdef DEEP_SLEEP
|
||||
DBG(" Deep sleeping.");
|
||||
#ifdef ESP32
|
||||
esp_sleep_enable_timer_wakeup(sleep_time * 1000000);
|
||||
esp_deep_sleep_start();
|
||||
#endif
|
||||
#ifdef ESP8266
|
||||
ESP.deepSleep(sleep_time * 1000000);
|
||||
#endif
|
||||
#endif
|
||||
DBG(" Delaying.");
|
||||
delay(sleep_time * 1000);
|
||||
}
|
30
Sensors/GenericGPS_fdrs/sensor_setup.h
Normal file
30
Sensors/GenericGPS_fdrs/sensor_setup.h
Normal file
@ -0,0 +1,30 @@
|
||||
// FARM DATA RELAY SYSTEM
|
||||
//
|
||||
// Sensor Configuration
|
||||
// (This file will soon be known as 'sensor_config.h')
|
||||
//
|
||||
|
||||
#include <fdrs_globals.h> //Uncomment when you install the globals file
|
||||
|
||||
#define READING_ID 1 //Unique ID for this sensor
|
||||
#define GTWY_MAC 0x04 //Address of the nearest gateway
|
||||
|
||||
//#define USE_ESPNOW
|
||||
#define USE_LORA
|
||||
#define DEEP_SLEEP
|
||||
//#define POWER_CTRL 14
|
||||
#define DEBUG
|
||||
|
||||
//LoRa Configuration
|
||||
#define SCK 5
|
||||
#define MISO 19
|
||||
#define MOSI 27
|
||||
#define SS 18
|
||||
// RST: 23 for T-Beam or Paxcounter, 14 for Lora32 V1
|
||||
#define RST 23
|
||||
#define DIO0 26
|
||||
//433E6 for Asia
|
||||
//866E6 for Europe
|
||||
//915E6 for North America
|
||||
#define BAND 866E6
|
||||
#define SF 7
|
Loading…
Reference in New Issue
Block a user