Initial Commit

This commit is contained in:
Charles Showalter 2024-08-19 09:29:50 -07:00
parent f00ea6624f
commit d133abefec
18 changed files with 1912 additions and 0 deletions

5
.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
.pio
.vscode/.browse.c_cpp.db*
.vscode/c_cpp_properties.json
.vscode/launch.json
.vscode/ipch

10
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,10 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"platformio.platformio-ide"
],
"unwantedRecommendations": [
"ms-vscode.cpptools-extension-pack"
]
}

247
include/astm.h Normal file
View File

@ -0,0 +1,247 @@
/*
___ _ ___ _
| _ \ ( )_ | _ \ (_ )
| (_) ) _ | _) _ _ __ | (_) ) __ _ _ | | ___ ___
| / / _ \| | / _ \( __) | / / __ \/ _ )| |/ _ _ \
| |\ \( (_) ) |_( (_) ) | | |\ \( ___/ (_| || || ( ) ( ) |
(_) (_)\___/ \__)\___/(_) (_) (_)\____)\__ _)___)_) (_) (_)
* This file is part of Rotor Realm RemoteID
*
* Copyright (c) 2024 Rotor Realm
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
// Definitions and structures required by ASTM F3411-22a standard
#ifndef ASTM_F3411_22A_H
#define ASTM_F3411_22A_H
// Enumeration for message types in ASTM F3411-22a standard
typedef enum Messages {
msBasicID = 0x0, // Basic ID message type (0x0)
msLocation = 0x1, // Location/Vector message type (0x1)
msAuthentication = 0x2, // Authentication message type (0x2)
msSelfID = 0x3, // Self ID message type (0x3)
msSystem = 0x4, // System message type (0x4)
msOperatorID = 0x5, // Operator ID message type (0x5)
msMessagePack = 0xF // Message Pack message type (0xF)
} msType;
// Enumeration for different types of IDs in ASTM F3411-22a standard
typedef enum ID {
idNone = 0, // No ID type (0)
idSerial = 1, // Serial number ID type (1)
idCAA = 2, // CAA (Civil Aviation Authority) compliant ID type (2)
idUTM = 3, // UTM (Unmanned Traffic Management) compliant ID type (3)
idSession = 4 // Session ID type (4)
} idTypes;
// Enumeration for different types of Unmanned Aircraft (UA) in ASTM F3411-22a standard
typedef enum UA {
uaUndefined = 0, // Undefined UA type (0)
uaAeroplane = 1, // Aeroplane UA type (1)
uaMultirotor = 2, // Multirotor UA type (2)
uaGyroplane = 3, // Gyroplane UA type (3)
uaFixedWing = 4, // Fixed-wing UA type (4)
uaOrnithopter = 5, // Ornithopter UA type (5)
uaGlider = 6, // Glider UA type (6)
uaKite = 7, // Kite UA type (7)
uaFreeBalloon = 8, // Free balloon UA type (8)
uaCaptiveBalloon = 9, // Captive balloon UA type (9)
uaAirship = 10, // Airship UA type (10)
uaFreeFall = 11, // Free-fall UA type (11)
uaRocket = 12, // Rocket UA type (12)
uaTethered = 13, // Tethered UA type (13)
uaGround = 14, // Ground UA type (14)
uaOther = 15 // Other UA type (15)
} uaTypes;
// Enumeration for different types of Operational Status in ASTM F3411-22a standard
typedef enum OperationalStatus {
osUndeclared = 0, // Undeclared operational status (0)
osGround = 1, // Ground operational status (1)
osAirborne = 2, // Airborne operational status (2)
osEmergency = 3, // Emergency operational status (3)
osSystemFailure = 4 // System failure operational status (4)
} osTypes;
// Enumeration for different types of Operator Location in ASTM F3411-22a standard
typedef enum OperatorLocation {
olTakeoff = 0, // Operator location at the takeoff point (0)
olDynamic = 1, // Dynamic operator location (moving) (1)
olFixed = 2 // Fixed operator location (stationary) (2)
} olTypes;
// Enumeration for different classification types in ASTM F3411-22a standard
// Enumeration for different types of Classification in ASTM F3411-22a standard
typedef enum Classification {
cfUndeclared = 0, // Undeclared classification (0)
cfEuropeanUnion = 1, // European Union classification (1)
// 2 - 7 Reserved for future use
} cfTypes;
// Enumeration for different types of Categories in ASTM F3411-22a standard
typedef enum Categories {
euUndefined = 0, // Undefined category (0)
euOpen = 1, // Open category (1)
euSpecific = 2, // Specific category (2)
euCertified = 3 // Certified category (3)
// 4 - 15 Reserved for future use
} euTypes;
// Enumeration for different types of Classes in ASTM F3411-22a standard
typedef enum Classes {
clUndefined = 0, // Undefined class (0)
clClass0 = 1, // Class 0 (1)
clClass1 = 2, // Class 1 (2)
clClass2 = 3, // Class 2 (3)
clClass3 = 4, // Class 3 (4)
clClass4 = 5, // Class 4 (5)
clClass5 = 6 // Class 5 (6)
// 7 - 15 Reserved for future use
} clTypes;
// Structure for Message Header as defined in ASTM F3411-22a standard
typedef struct MessageHeader {
uint8_t messageType_protocolVersion; // Bits 7..4: Message Type, Bits 3..0: Protocol Version
} MessageHeader;
// Structure for Basic ID Message as defined in ASTM F3411-22a standard
typedef struct BasicIDMessage {
MessageHeader header; // 1 byte
uint8_t idType_uaType; // ID Type (upper 4 bits) and UA Type (lower 4 bits) - 1 byte
uint8_t uasID[20]; // UAS ID (20 bytes)
uint8_t reserved[3]; // Reserved bytes (3 bytes)
} BasicIDMessage;
// Structure for Location/Vector Message as defined in ASTM F3411-22a standard
typedef struct LocationMessage {
MessageHeader header; // 1 byte
uint8_t status; // 1 byte
uint8_t trackDirection; // 1 byte
uint8_t speed; // 1 byte
int8_t verticalSpeed; // 1 byte
int32_t latitude; // 4 bytes
int32_t longitude; // 4 bytes
int16_t pressureAltitude; // 2 bytes
int16_t geodeticAltitude; // 2 bytes
int16_t height; // 2 bytes
uint8_t horVertAccuracy; // 1 byte (Combined Horizontal and Vertical Accuracy)
uint8_t baroSpeedAccuracy; // 1 byte (Combined Baro Altitude Accuracy and Speed Accuracy)
uint16_t timestamp; // 2 bytes
uint8_t reservedTimestampAccuracy; // 1 byte (Combined Reserved and Timestamp Accuracy)
uint8_t reserved; // 1 byte
} __attribute__((packed)) LocationMessage;
// Structure for Authentication Message as defined in ASTM F3411-22a standard
typedef struct AuthenticationMessage {
MessageHeader header; // Message header
uint8_t authType; // Authentication Type
uint8_t authData[23]; // Authentication Data
} AuthenticationMessage;
// Structure for Self-ID Message as defined in ASTM F3411-22a standard
typedef struct SelfIDMessage {
MessageHeader header; // Message header
uint8_t descriptionType; // Description Type
char description[23]; // ASCII Text Description (padded with nulls)
} SelfIDMessage;
// Structure for System Message as defined in ASTM F3411-22a standard
typedef struct SystemMessage {
MessageHeader header; // Message header
uint8_t flags; // Flags (1 byte)
int32_t operatorLatitude; // Latitude of Remote Pilot (4 bytes)
int32_t operatorLongitude; // Longitude of Remote Pilot (4 bytes)
uint16_t areaCount; // Number of aircraft in Area (2 bytes)
uint16_t areaRadius; // Radius of area (2 bytes)
int16_t areaCeiling; // Area ceiling (2 bytes)
int16_t areaFloor; // Area floor (2 bytes)
uint8_t classification; // Classification (1 byte)
int16_t operatorAltitude; // Altitude of Remote Pilot (2 bytes)
uint32_t timestamp; // Timestamp (4 bytes)
uint8_t reserved; // Reserved (1 byte)
} __attribute__((packed)) SystemMessage;
// Structure for Operator-ID Message as defined in ASTM F3411-22a standard
typedef struct OperatorIDMessage {
MessageHeader header; // Message header
uint8_t operatorIDType; // Operator ID Type (0: Operator ID, 1-200: Reserved, 201-255: Available for private use)
char operatorID[20]; // ASCII Text for Operator ID (padded with nulls)
uint8_t reserved[3]; // Reserved bytes
} OperatorIDMessage;
// Horizontal Accuracy Enumeration
typedef enum HorizontalAccuracy {
H_ACC_UNKNOWN = 0,
H_ACC_10_NM = 1,
H_ACC_4_NM = 2,
H_ACC_2_NM = 3,
H_ACC_1_NM = 4,
H_ACC_0_5_NM = 5,
H_ACC_0_3_NM = 6,
H_ACC_0_1_NM = 7,
H_ACC_0_05_NM = 8,
H_ACC_30_M = 9,
H_ACC_10_M = 10,
H_ACC_3_M = 11,
H_ACC_1_M = 12,
H_ACC_RESERVED = 13
} HorizontalAccuracy;
// Vertical Accuracy Enumeration
typedef enum VerticalAccuracy {
V_ACC_UNKNOWN = 0,
V_ACC_150_M = 1,
V_ACC_45_M = 2,
V_ACC_25_M = 3,
V_ACC_10_M = 4,
V_ACC_3_M = 5,
V_ACC_1_M = 6
} VerticalAccuracy;
// Speed Accuracy Enumeration
typedef enum SpeedAccuracy {
S_ACC_UNKNOWN = 0,
S_ACC_10_M_S = 1,
S_ACC_3_M_S = 2,
S_ACC_1_M_S = 3,
S_ACC_0_3_M_S = 4
} SpeedAccuracy;
// Define VendorSpecificTag structure
typedef struct VendorSpecificTag {
uint8_t length;
uint8_t oui[3];
uint8_t vendType;
uint8_t msgCounter;
uint8_t advData[256]; // Example size, should be adjusted according to the actual requirement
} VendorSpecificTag;
typedef struct MessagePack {
MessageHeader header; // Message header
uint8_t messageSize; // Message Size (25 Bytes)
uint8_t numMessages; // Number of messages in the pack (up to 9)
uint8_t messages[9][25]; // Array of messages, up to 9 messages, each 25 bytes long
} MessagePack;
#endif // ASTM_F3411_22A_H

38
include/display.h Normal file
View File

@ -0,0 +1,38 @@
/*
___ _ ___ _
| _ \ ( )_ | _ \ (_ )
| (_) ) _ | _) _ _ __ | (_) ) __ _ _ | | ___ ___
| / / _ \| | / _ \( __) | / / __ \/ _ )| |/ _ _ \
| |\ \( (_) ) |_( (_) ) | | |\ \( ___/ (_| || || ( ) ( ) |
(_) (_)\___/ \__)\___/(_) (_) (_)\____)\__ _)___)_) (_) (_)
* This file is part of Rotor Realm RemoteID
*
* Copyright (c) 2024 Rotor Realm
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef DISPLAY_H
#define DISPLAY_H
#include <Arduino.h>
void displaySetup(const char* ssid);
void displayTask(void * parameters);
#endif

246
include/enc.h Normal file
View File

@ -0,0 +1,246 @@
/*
___ _ ___ _
| _ \ ( )_ | _ \ (_ )
| (_) ) _ | _) _ _ __ | (_) ) __ _ _ | | ___ ___
| / / _ \| | / _ \( __) | / / __ \/ _ )| |/ _ _ \
| |\ \( (_) ) |_( (_) ) | | |\ \( ___/ (_| || || ( ) ( ) |
(_) (_)\___/ \__)\___/(_) (_) (_)\____)\__ _)___)_) (_) (_)
* This file is part of Rotor Realm RemoteID
*
* Copyright (c) 2024 Rotor Realm
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef ENCODERS_H
#define ENCODERS_H
#include <astm.h>
// Function declarations for encoding messages
inline void encodeMessageHeader(MessageHeader *header, uint8_t messageType, uint8_t protocolVersion);
inline void encodeBasicIDMessage(BasicIDMessage *msg, idTypes idType, uaTypes uaType, const char *uasID);
inline void encodeLocationMessage(LocationMessage *msg, uint8_t status, uint8_t trackDirection, uint8_t speed, int8_t verticalSpeed, float latitude, float longitude, float pressureAltitude, float geodeticAltitude, int16_t height, uint32_t horAccuracy, uint32_t vertAccuracy, uint32_t speedAccuracy, uint16_t timestamp);
inline void encodeAuthenticationMessage(AuthenticationMessage *msg, uint8_t authType, const uint8_t *authData);
inline void encodeSelfIDMessage(SelfIDMessage *msg, uint8_t descriptionType, const char *description);
inline void encodeSystemMessage(SystemMessage *msg, uint8_t operatorLocationType, int32_t operatorLatitude, int32_t operatorLongitude, uint16_t areaCount, uint16_t areaRadius, int16_t areaCeiling, int16_t areaFloor, uint8_t classificationType, uint8_t uaCategory, uint8_t uaClass, int16_t operatorAltitude, uint32_t timestamp);
inline void encodeOperatorIDMessage(OperatorIDMessage *msg, uint8_t operatorIDType, const char *operatorID);
inline void encodeVendorSpecificTag(VendorSpecificTag *vendorTag, MessagePack *messagePack);
inline uint8_t encodeDirection(uint16_t direction, uint8_t *directionSegment);
inline uint8_t encodeSpeed(float speed, uint8_t *speedMultiplier);
inline int32_t convertLatLonFormat(float value);
inline int8_t encodeVerticalSpeed(float verticalSpeed);
inline uint16_t encodeAltitude(float altitude);
inline uint16_t encodeTimestamp(float timestamp);
inline uint8_t convertHorizontalAccuracy(uint32_t accuracy) {
if (accuracy >= 18520000) return 0;
if (accuracy >= 7408000) return 1;
if (accuracy >= 3704000) return 2;
if (accuracy >= 1852000) return 3;
if (accuracy >= 926000) return 4;
if (accuracy >= 555600) return 5;
if (accuracy >= 185200) return 6;
if (accuracy >= 92600) return 7;
if (accuracy >= 30000) return 8;
if (accuracy >= 10000) return 9;
if (accuracy >= 3000) return 10;
if (accuracy >= 1000) return 11;
if (accuracy >= 100) return 12;
return 13;
}
inline uint8_t convertVerticalAccuracy(uint32_t accuracy) {
if (accuracy >= 15000) return 0;
if (accuracy >= 4500) return 1;
if (accuracy >= 2500) return 2;
if (accuracy >= 1000) return 3;
if (accuracy >= 300) return 4;
if (accuracy >= 100) return 5;
return 6;
}
inline uint8_t convertSpeedAccuracy(uint32_t accuracy) {
if (accuracy >= 10000) return 0;
if (accuracy >= 3000) return 1;
if (accuracy >= 1000) return 2;
if (accuracy >= 300) return 3;
return 4;
}
inline uint8_t encodeTrackDirection(float course) {
if (course < 0 || course > 359) {
return 0; // Default value if the course is out of range
}
uint8_t direction = static_cast<uint8_t>(course);
if (direction > 179) {
direction -= 180;
}
return direction;
}
// Function definitions
inline void encodeMessageHeader(MessageHeader *header, uint8_t messageType, uint8_t protocolVersion) {
header->messageType_protocolVersion = (messageType << 4) | (protocolVersion & 0x0F);
}
inline void encodeBasicIDMessage(BasicIDMessage *msg, idTypes idType, uaTypes uaType, const char *uasID) {
encodeMessageHeader(&msg->header, msBasicID, 0x01);
msg->idType_uaType = (idType << 4) | (uaType & 0x0F);
memset(msg->uasID, 0, sizeof(msg->uasID));
strncpy((char *)msg->uasID, uasID, sizeof(msg->uasID) - 1);
msg->uasID[sizeof(msg->uasID) - 1] = '\0';
memset(msg->reserved, 0, sizeof(msg->reserved));
}
inline void encodeLocationMessage(LocationMessage *msg, uint8_t status, uint8_t trackDirection, uint8_t speed, int8_t verticalSpeed, float latitude, float longitude, float pressureAltitude, float geodeticAltitude, int16_t height, uint32_t horAccuracy, uint32_t vertAccuracy, uint32_t speedAccuracy, uint16_t timestamp) {
encodeMessageHeader(&msg->header, msLocation, 0x01);
msg->status = (status << 4);
msg->trackDirection = encodeTrackDirection(trackDirection);
msg->speed = speed;
msg->verticalSpeed = verticalSpeed;
msg->latitude = static_cast<int32_t>(latitude * 1e7); // Convert degrees to degrees * 10^7
msg->longitude = static_cast<int32_t>(longitude * 1e7); // Convert degrees to degrees * 10^7
msg->pressureAltitude = encodeAltitude(pressureAltitude);
msg->geodeticAltitude = encodeAltitude(geodeticAltitude);
msg->height = height;
msg->horVertAccuracy = (convertVerticalAccuracy(vertAccuracy) << 4) | (convertHorizontalAccuracy(horAccuracy) & 0x0F);
msg->baroSpeedAccuracy = (convertSpeedAccuracy(speedAccuracy) << 4) | (convertSpeedAccuracy(speedAccuracy) & 0x0F);
msg->timestamp = timestamp;
msg->reservedTimestampAccuracy = 0x00;
msg->reserved = 0x00;
}
inline void encodeAuthenticationMessage(AuthenticationMessage *msg, uint8_t authType, const uint8_t *authData) {
encodeMessageHeader(&msg->header, msAuthentication, 0x1);
msg->authType = authType;
memcpy(msg->authData, authData, sizeof(msg->authData));
}
inline void encodeSelfIDMessage(SelfIDMessage *msg, uint8_t descriptionType, const char *description) {
encodeMessageHeader(&msg->header, msSelfID, 0x1);
msg->descriptionType = descriptionType;
strncpy(msg->description, description, sizeof(msg->description));
if (strlen(description) < sizeof(msg->description)) {
memset(msg->description + strlen(description), 0, sizeof(msg->description) - strlen(description));
}
}
inline void encodeSystemMessage(SystemMessage *msg, uint8_t operatorLocationType, int32_t operatorLatitude, int32_t operatorLongitude, uint16_t areaCount, uint16_t areaRadius, int16_t areaCeiling, int16_t areaFloor, uint8_t classificationType, uint8_t uaCategory, uint8_t uaClass, int16_t operatorAltitude, uint32_t timestamp) {
encodeMessageHeader(&msg->header, msSystem, 0x1);
msg->flags = (classificationType << 4) | operatorLocationType;
msg->operatorLatitude = operatorLatitude;
msg->operatorLongitude = operatorLongitude;
msg->areaCount = areaCount;
msg->areaRadius = areaRadius;
msg->areaCeiling = areaCeiling;
msg->areaFloor = areaFloor;
msg->classification = (uaCategory << 4) | uaClass;
msg->operatorAltitude = operatorAltitude;
msg->timestamp = timestamp;
msg->reserved = 0;
}
inline void encodeOperatorIDMessage(OperatorIDMessage *msg, uint8_t operatorIDType, const char *operatorID) {
encodeMessageHeader(&msg->header, msOperatorID, 0x1);
msg->operatorIDType = operatorIDType;
strncpy(msg->operatorID, operatorID, sizeof(msg->operatorID));
if (strlen(operatorID) < sizeof(msg->operatorID)) {
memset(msg->operatorID + strlen(operatorID), 0, sizeof(msg->operatorID) - strlen(operatorID));
}
memset(msg->reserved, 0, sizeof(msg->reserved));
}
inline void encodeVendorSpecificTag(VendorSpecificTag *vendorTag, MessagePack *messagePack) {
vendorTag->length = 3 + 1 + 1 + (messagePack->numMessages * messagePack->messageSize);
vendorTag->oui[0] = 0xFA;
vendorTag->oui[1] = 0x0B;
vendorTag->oui[2] = 0xBC;
vendorTag->vendType = 0x0D;
memcpy(vendorTag->advData, messagePack, sizeof(MessageHeader) + 1 + 1 + (messagePack->numMessages * messagePack->messageSize));
}
inline uint8_t encodeDirection(uint16_t direction, uint8_t *directionSegment) {
uint8_t encodedValue;
if (direction < 180) {
encodedValue = direction;
*directionSegment = 0;
} else {
encodedValue = direction - 180;
*directionSegment = 1;
}
return encodedValue;
}
inline uint8_t encodeSpeed(float speed, uint8_t *speedMultiplier) {
uint8_t encodedValue;
if (speed <= 255 * 0.25) {
encodedValue = speed / 0.25;
*speedMultiplier = 0;
} else if (speed > 255 * 0.25 && speed < 254.25) {
encodedValue = (speed - (255 * 0.25)) / 0.75;
*speedMultiplier = 1;
} else {
encodedValue = 254;
*speedMultiplier = 1;
}
return encodedValue;
}
inline int32_t convertLatLonFormat(float value) {
// Check if the value is already in the format of degrees * 10^7
if (value > 1000000.0 || value < -1000000.0) {
return static_cast<int32_t>(value);
} else if (value > 360.0 || value < -360.0) {
// If value is in decimal format (e.g., 12345.6789), convert it to degrees first
int degrees = static_cast<int>(value / 100);
float minutes = value - (degrees * 100);
float decimalDegrees = degrees + (minutes / 60.0);
return static_cast<int32_t>(decimalDegrees * 1e7);
} else {
// Convert degrees to degrees * 10^7
return static_cast<int32_t>(value * 1e7);
}
}
inline int8_t encodeVerticalSpeed(float verticalSpeed) {
if (verticalSpeed > 62.0) {
verticalSpeed = 62.0;
} else if (verticalSpeed < -62.0) {
verticalSpeed = -62.0;
}
float dividedValue = verticalSpeed / 0.5;
int8_t encodedValue = static_cast<int8_t>(dividedValue);
return encodedValue;
}
inline uint16_t encodeAltitude(float altitude) {
if (altitude == -1000) {
return 0;
}
return (altitude + 1000) / 0.5;
}
inline uint16_t encodeTimestamp(float timestamp) {
return timestamp * 10;
}
#endif // ENCODERS_H

42
include/gps.h Normal file
View File

@ -0,0 +1,42 @@
/*
___ _ ___ _
| _ \ ( )_ | _ \ (_ )
| (_) ) _ | _) _ _ __ | (_) ) __ _ _ | | ___ ___
| / / _ \| | / _ \( __) | / / __ \/ _ )| |/ _ _ \
| |\ \( (_) ) |_( (_) ) | | |\ \( ___/ (_| || || ( ) ( ) |
(_) (_)\___/ \__)\___/(_) (_) (_)\____)\__ _)___)_) (_) (_)
* This file is part of Rotor Realm RemoteID
*
* Copyright (c) 2024 Rotor Realm
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef GPS_H
#define GPS_H
#include <Arduino.h>
#include "nema.h" // Include the header file where GGAData is defined
// External declaration of ggaData
extern GGAData ggaData;
extern HardwareSerial GPSSerial;
void gpsSetup();
void gpsTask(void * parameters);
#endif

54
include/init.h Normal file
View File

@ -0,0 +1,54 @@
/*
___ _ ___ _
| _ \ ( )_ | _ \ (_ )
| (_) ) _ | _) _ _ __ | (_) ) __ _ _ | | ___ ___
| / / _ \| | / _ \( __) | / / __ \/ _ )| |/ _ _ \
| |\ \( (_) ) |_( (_) ) | | |\ \( ___/ (_| || || ( ) ( ) |
(_) (_)\___/ \__)\___/(_) (_) (_)\____)\__ _)___)_) (_) (_)
* This file is part of Rotor Realm RemoteID
*
* Copyright (c) 2024 Rotor Realm
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef INIT_H
#define INIT_H
#include <Arduino.h>
#include <ArduinoJson.h>
struct OperatorInfo {
String OperatorID;
int32_t OperatorLatitude;
int32_t OperatorLongitude;
String OperatorTimezone;
};
struct Config {
int enableBT;
int enableWiFi;
OperatorInfo operatorInfo;
};
extern Config config;
void loadConfig();
#endif // INIT_H

108
include/nema.h Normal file
View File

@ -0,0 +1,108 @@
/*
___ _ ___ _
| _ \ ( )_ | _ \ (_ )
| (_) ) _ | _) _ _ __ | (_) ) __ _ _ | | ___ ___
| / / _ \| | / _ \( __) | / / __ \/ _ )| |/ _ _ \
| |\ \( (_) ) |_( (_) ) | | |\ \( ___/ (_| || || ( ) ( ) |
(_) (_)\___/ \__)\___/(_) (_) (_)\____)\__ _)___)_) (_) (_)
* This file is part of Rotor Realm RemoteID
*
* Copyright (c) 2024 Rotor Realm
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef NEMA_H
#define NEMA_H
#include <Arduino.h>
struct GGAData {
String time;
double latitude;
char latDir;
double longitude;
char lonDir;
int fixQuality;
int numSatellites;
double hdop;
double altitude;
char altUnit;
double geoidHeight;
char geoUnit;
double dgpsAge;
int dgpsStationId;
};
struct RMCData {
String time;
char status;
double latitude;
char latDir;
double longitude;
char lonDir;
double speed;
double course;
String date;
double magVar;
char magVarDir;
char mode;
};
struct GSAData {
char mode1;
int mode2;
int sv[12];
double pdop;
double hdop;
double vdop;
};
struct GSVData {
int numMessages;
int messageNum;
int numSVs;
struct {
int svID;
int elevation;
int azimuth;
int snr;
} svInfo[4];
};
struct NAVACCData {
String time;
char status;
uint32_t pAcc; // Horizontal Accuracy
uint32_t vAcc; // Vertical Accuracy
uint32_t cAcc; // Speed Accuracy
};
void parseGGA(String nmea, GGAData &ggaData);
void parseRMC(String nmea, RMCData &rmcData);
void parseGSA(String nmea, GSAData &gsaData);
void parseGSV(String nmea, GSVData &gsvData);
void parseNAVACC(String nmea, NAVACCData &navaccData);
void parseNMEA(String nmea, GGAData &ggaData, RMCData &rmcData, GSAData &gsaData, GSVData &gsvData, NAVACCData &navaccData);
double convertToDecimalDegrees(double coordinate, char direction);
double parseDouble(String value);
int parseInt(String value);
#endif // NEMA_H

71
include/wifiBeacon.h Normal file
View File

@ -0,0 +1,71 @@
/*
___ _ ___ _
| _ \ ( )_ | _ \ (_ )
| (_) ) _ | _) _ _ __ | (_) ) __ _ _ | | ___ ___
| / / _ \| | / _ \( __) | / / __ \/ _ )| |/ _ _ \
| |\ \( (_) ) |_( (_) ) | | |\ \( ___/ (_| || || ( ) ( ) |
(_) (_)\___/ \__)\___/(_) (_) (_)\____)\__ _)___)_) (_) (_)
* This file is part of Rotor Realm RemoteID
*
* Copyright (c) 2024 Rotor Realm
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef WIFI_H
#define WIFI_H
#include <Arduino.h>
#include <enc.h>
#include <astm.h>
// Define the WiFi channel and output power
extern const int wifiChannel;
extern const int wifiOutputPower;
extern const int ledPin;
// Define UA Information
#define uasIDType 1 // Serial
#define uasType 2 // Multirotor
// Define Location Information
// #define uasStatus 2
// #define uasLatitude 336970783 // UAS Latitude
// #define uasLongitude -1171853531 // UAS Longitude
// #define uasAltitude 0 // UAS Altitude in meters
// #define uasHorizontalSpeed 60 // Horizontal Speed in cm/s
// #define uasVerticalSpeed 30 // Vertical Speed in cm/s
// #define uasTrack 180 // Track direction in degrees
// Define Operator Information
// #define operatorID "FC:WorldDrknss"
// #define operatorLatitude 336970783
// #define operatorLongitude -1171853531
// Define Self ID Information
#define selfIdType 0
#define selfIdDescription "Recreational"
void wifiBeaconSetup(const char* ssid);
void wifiBeaconTask(void* parameters);
void printRawBytes(uint8_t* data, size_t length);
void constructBeaconFrame(const char* ssid, int channel, int beaconInterval);
void encodeVendorSpecificElement(const char* ssid);
#endif // WIFI_H

46
lib/README Normal file
View File

@ -0,0 +1,46 @@
This directory is intended for project specific (private) libraries.
PlatformIO will compile them to static libraries and link into executable file.
The source code of each library should be placed in an own separate directory
("lib/your_library_name/[here are source files]").
For example, see a structure of the following two libraries `Foo` and `Bar`:
|--lib
| |
| |--Bar
| | |--docs
| | |--examples
| | |--src
| | |- Bar.c
| | |- Bar.h
| | |- library.json (optional, custom build options, etc) https://docs.platformio.org/page/librarymanager/config.html
| |
| |--Foo
| | |- Foo.c
| | |- Foo.h
| |
| |- README --> THIS FILE
|
|- platformio.ini
|--src
|- main.c
and a contents of `src/main.c`:
```
#include <Foo.h>
#include <Bar.h>
int main (void)
{
...
}
```
PlatformIO Library Dependency Finder will find automatically dependent
libraries scanning project source files.
More information about PlatformIO Library Dependency Finder
- https://docs.platformio.org/page/librarymanager/ldf.html

18
platformio.ini Normal file
View File

@ -0,0 +1,18 @@
; PlatformIO Project Configuration File
;
; Build options: build flags, source filter
; Upload options: custom upload port, speed and extra flags
; Library options: dependencies, extra library storages
; Advanced options: extra scripting
;
; Please visit documentation for the other options and examples
; https://docs.platformio.org/page/projectconf.html
[env:heltec_wireless_stick]
platform = espressif32
board = heltec_wireless_stick
framework = arduino
lib_deps =
heltecautomation/Heltec ESP32 Dev-Boards@^2.0.2
bblanchon/ArduinoJson@^7.1.0
paulstoffregen/Time@^1.6.1

210
src/display.cpp Normal file
View File

@ -0,0 +1,210 @@
/*
___ _ ___ _
| _ \ ( )_ | _ \ (_ )
| (_) ) _ | _) _ _ __ | (_) ) __ _ _ | | ___ ___
| / / _ \| | / _ \( __) | / / __ \/ _ )| |/ _ _ \
| |\ \( (_) ) |_( (_) ) | | |\ \( ___/ (_| || || ( ) ( ) |
(_) (_)\___/ \__)\___/(_) (_) (_)\____)\__ _)___)_) (_) (_)
* This file is part of Rotor Realm RemoteID
*
* Copyright (c) 2024 Rotor Realm
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
//display.cpp
#include "HT_st7735.h"
#include "init.h"
#include "gps.h" // Include the header file where ggaData is declared
#include <string.h> // Include string.h for strlen and strcmp functions
#include <time.h>
extern TaskHandle_t wifiBeaconTaskHandle; // Declare the task handle as external
HT_st7735 tft;
extern Config config;
// Variables to store previous values
int prevNumSatellites = -1;
int prevNumSatellitesLength = strlen("PNDNG");
double prevLatitude = -1;
int prevLatitudeLength = strlen("PNDNG");
double prevLongitude = -1;
int prevLongitudeLength = strlen("PNDNG");
char prevTime[20] = "PNDNG";
int prevTimeLength = strlen(prevTime);
// Variables to store previous WiFi status
char prevWifiStatus[10] = "PNDNG";
int prevWifiStatusLength = strlen(prevWifiStatus);
#define ST7735_PURPLE 0x780F // This is an example for purple
int timezoneStringToOffset(const String& timezone) {
if (timezone == "PST") return -8;
if (timezone == "PDT") return -7;
if (timezone == "EST") return -5;
if (timezone == "EDT") return -4;
if (timezone == "CST") return -6;
if (timezone == "CDT") return -5;
// Add other timezones as needed
return 0; // Default to UTC if the timezone is not recognized
}
void displaySetup(const char* ssid) {
int col1 = 0;
int col2 = 30;
// Initialize Screen and Fill Black
tft.st7735_init();
tft.st7735_fill_screen(ST7735_BLACK);
// Beacon and FC Status
tft.st7735_write_str(col1, 0, "BTX:", Font_7x10, ST7735_PURPLE, ST7735_BLACK);
tft.st7735_write_str(col2, 0, "PNDNG", Font_7x10, ST7735_RED, ST7735_BLACK);
tft.st7735_write_str(85, 0, "FC:", Font_7x10, ST7735_PURPLE, ST7735_BLACK);
tft.st7735_write_str(110, 0, "PNDNG", Font_7x10, ST7735_RED, ST7735_BLACK);
// Satellites
tft.st7735_write_str(col1, 13, "SAT:", Font_7x10, ST7735_PURPLE, ST7735_BLACK);
tft.st7735_write_str(col2, 13, "PNDNG", Font_7x10, ST7735_RED, ST7735_BLACK);
tft.st7735_write_str(85, 13, "TM:", Font_7x10, ST7735_PURPLE, ST7735_BLACK);
tft.st7735_write_str(110, 13, "PNDNG", Font_7x10, ST7735_RED, ST7735_BLACK);
// Lat
tft.st7735_write_str(col1, 26, "LAT:", Font_7x10, ST7735_PURPLE, ST7735_BLACK);
tft.st7735_write_str(col2, 26, "PNDNG", Font_7x10, ST7735_RED, ST7735_BLACK);
// Lon
tft.st7735_write_str(col1, 39, "LNG:", Font_7x10, ST7735_PURPLE, ST7735_BLACK);
tft.st7735_write_str(col2, 39, "PNDNG", Font_7x10, ST7735_RED, ST7735_BLACK);
// Aircraft Serial
tft.st7735_write_str(col1, 52, "MFR:", Font_7x10, ST7735_PURPLE, ST7735_BLACK);
tft.st7735_write_str(col2, 52, "RRDI", Font_7x10, ST7735_RED, ST7735_BLACK); // Show RRDI
tft.st7735_write_str(col1, 65, "S/N:", Font_7x10, ST7735_PURPLE, ST7735_BLACK);
tft.st7735_write_str(col2, 65, ssid + 4, Font_7x10, ST7735_RED, ST7735_BLACK); // Show the remaining of SSID
}
String formatTime(int value) {
if (value < 10) return "0" + String(value);
return String(value);
}
void displayTask(void* parameters) {
Serial.println("Display Task started");
for (;;) {
// Update the display with ggaData only if values have changed
char buffer[20];
// Update number of satellites if it has changed
if (ggaData.numSatellites > 0 && ggaData.numSatellites != prevNumSatellites) {
snprintf(buffer, sizeof(buffer), "%d", ggaData.numSatellites);
int newLength = strlen(buffer);
tft.st7735_write_str(30, 13, buffer, Font_7x10, ST7735_RED, ST7735_BLACK);
// Clear any extra characters
for (int i = newLength; i < prevNumSatellitesLength; i++) {
tft.st7735_write_str(30 + i * 7, 13, " ", Font_7x10, ST7735_BLACK, ST7735_BLACK);
}
prevNumSatellites = ggaData.numSatellites;
prevNumSatellitesLength = newLength;
} else if (ggaData.numSatellites == 0 && prevNumSatellites != 0) {
tft.st7735_write_str(30, 13, "PNDNG", Font_7x10, ST7735_RED, ST7735_BLACK);
prevNumSatellites = 0;
prevNumSatellitesLength = strlen("PNDNG");
}
// Update latitude if it has changed
if (ggaData.latitude != prevLatitude) {
snprintf(buffer, sizeof(buffer), "%.5f", ggaData.latitude);
int newLength = strlen(buffer);
tft.st7735_write_str(30, 26, buffer, Font_7x10, ST7735_RED, ST7735_BLACK);
// Clear any extra characters
for (int i = newLength; i < prevLatitudeLength; i++) {
tft.st7735_write_str(30 + i * 7, 26, " ", Font_7x10, ST7735_BLACK, ST7735_BLACK);
}
prevLatitude = ggaData.latitude;
prevLatitudeLength = newLength;
} else if (ggaData.latitude == 0.0 && prevLatitude != 0.0) {
tft.st7735_write_str(30, 26, "PNDNG", Font_7x10, ST7735_RED, ST7735_BLACK);
prevLatitude = 0.0;
prevLatitudeLength = strlen("PNDNG");
}
// Update longitude if it has changed
if (ggaData.longitude != prevLongitude) {
snprintf(buffer, sizeof(buffer), "%.5f", ggaData.longitude);
int newLength = strlen(buffer);
tft.st7735_write_str(30, 39, buffer, Font_7x10, ST7735_RED, ST7735_BLACK);
// Clear any extra characters
for (int i = newLength; i < prevLongitudeLength; i++) {
tft.st7735_write_str(30 + i * 7, 39, " ", Font_7x10, ST7735_BLACK, ST7735_BLACK);
}
prevLongitude = ggaData.longitude;
prevLongitudeLength = newLength;
} else if (ggaData.longitude == 0.0 && prevLongitude != 0.0) {
tft.st7735_write_str(30, 39, "PNDNG", Font_7x10, ST7735_RED, ST7735_BLACK);
prevLongitude = 0.0;
prevLongitudeLength = strlen("PNDNG");
}
// Get the timezone offset from config
int timezoneOffset = timezoneStringToOffset(config.operatorInfo.OperatorTimezone);
// Convert GPS time to int
int hours = ggaData.time.substring(0, 2).toInt();
int minutes = ggaData.time.substring(2, 4).toInt();
// Apply the timezone offset
hours = (hours + timezoneOffset + 24) % 24; // Ensure the hours wrap around correctly
// Format the adjusted time back to a string
String formattedTime = formatTime(hours) + ":" + formatTime(minutes);
// Update GPS time if it has changed
if (strcmp(formattedTime.c_str(), prevTime) != 0) {
snprintf(buffer, sizeof(buffer), "%s", formattedTime.c_str());
int newLength = strlen(buffer);
tft.st7735_write_str(110, 13, buffer, Font_7x10, ST7735_RED, ST7735_BLACK);
// Clear any extra characters
for (int i = newLength; i < prevTimeLength; i++) {
tft.st7735_write_str(110 + i * 7, 13, " ", Font_7x10, ST7735_BLACK, ST7735_BLACK);
}
strcpy(prevTime, formattedTime.c_str());
prevTimeLength = newLength;
}
// Update WiFi Beacon Task status if it has changed
const char* wifiStatus = (eTaskGetState(wifiBeaconTaskHandle) != eSuspended) ? "TRSMTNG" : "PNDNG";
if (strcmp(wifiStatus, prevWifiStatus) != 0) {
int newLength = strlen(wifiStatus);
tft.st7735_write_str(30, 0, wifiStatus, Font_7x10, ST7735_RED, ST7735_BLACK);
// Clear any extra characters
for (int i = newLength; i < prevWifiStatusLength; i++) {
tft.st7735_write_str(30 + i * 7, 0, " ", Font_7x10, ST7735_BLACK, ST7735_BLACK);
}
strcpy(prevWifiStatus, wifiStatus);
prevWifiStatusLength = newLength;
}
vTaskDelay(1000 / portTICK_PERIOD_MS); // Delay to yield control
}
}

127
src/gps.cpp Normal file
View File

@ -0,0 +1,127 @@
/*
___ _ ___ _
| _ \ ( )_ | _ \ (_ )
| (_) ) _ | _) _ _ __ | (_) ) __ _ _ | | ___ ___
| / / _ \| | / _ \( __) | / / __ \/ _ )| |/ _ _ \
| |\ \( (_) ) |_( (_) ) | | |\ \( ___/ (_| || || ( ) ( ) |
(_) (_)\___/ \__)\___/(_) (_) (_)\____)\__ _)___)_) (_) (_)
* This file is part of Rotor Realm RemoteID
*
* Copyright (c) 2024 Rotor Realm
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "gps.h"
#include "nema.h"
HardwareSerial GPSSerial(1);
GGAData ggaData;
RMCData rmcData;
GSAData gsaData;
GSVData gsvData;
NAVACCData navaccData;
#define VGNSS_CTRL 3
void gpsSetup() {
pinMode(VGNSS_CTRL, OUTPUT);
digitalWrite(VGNSS_CTRL, HIGH);
GPSSerial.begin(115200, SERIAL_8N1, 33, 34); // RX, TX
Serial.println("GPS setup complete");
// Enable NAVACC and VTG
GPSSerial.print("$CFGMSG,0,5,1\r\n"); // Enable VTG
delay(1000); // Wait for the GPS to respond
GPSSerial.print("$CFGMSG,1,3,1\r\n"); // Enable NAVACC
delay(1000); // Wait for the GPS to respond
}
void gpsTask(void* parameters) {
Serial.println("GPS Task started");
for (;;) {
while (GPSSerial.available()) {
String nmea = GPSSerial.readStringUntil('\n');
parseNMEA(nmea, ggaData, rmcData, gsaData, gsvData, navaccData);
// Print all NMEA messages for debugging
// Serial.println(nmea);
if (nmea.startsWith("$GPGGA") || nmea.startsWith("$GBGGA") || nmea.startsWith("$GAGGA") || nmea.startsWith("$GLGGA") || nmea.startsWith("$GNGGA")) {
// Serial.print("Time: ");
// Serial.println(ggaData.time);
// Serial.print("GGA Latitude: ");
// Serial.println(ggaData.latitude);
// Serial.print("GGA Longitude: ");
// Serial.println(ggaData.longitude);
// Serial.println(ggaData.numSatellites);
} else if (nmea.startsWith("$GPRMC") || nmea.startsWith("$GBRMC") || nmea.startsWith("$GARMC") || nmea.startsWith("$GLRMC") || nmea.startsWith("$GNRMC")) {
// Serial.print("Time: ");
// Serial.println(rmcData.time);
// Serial.print("Status: ");
// Serial.println(rmcData.status);
// Serial.print("RMC Latitude: ");
// Serial.println(rmcData.latitude);
// Serial.print("RMC Longitude: ");
// Serial.println(rmcData.longitude);
} else if (nmea.startsWith("$GPGSA") || nmea.startsWith("$GBGSA") || nmea.startsWith("$GAGSA") || nmea.startsWith("$GLGSA") || nmea.startsWith("$GNGSA")) {
// Serial.print("Mode1: ");
// Serial.println(gsaData.mode1);
// Serial.print("Mode2: ");
// Serial.println(gsaData.mode2);
// Serial.print("PDOP: ");
// Serial.println(gsaData.pdop);
// Serial.print("HDOP: ");
// Serial.println(gsaData.hdop);
// Serial.print("VDOP: ");
// Serial.println(gsaData.vdop);
} else if (nmea.startsWith("$GPGSV") || nmea.startsWith("$GBGSV") || nmea.startsWith("$GAGSV") || nmea.startsWith("$GLGSV") || nmea.startsWith("$GNGSV")) {
// Serial.print("Number of Messages: ");
// Serial.println(gsvData.numMessages);
// Serial.print("Message Number: ");
// Serial.println(gsvData.messageNum);
// Serial.print("Number of SVs: ");
// Serial.println(gsvData.numSVs);
// for (int i = 0; i < 4; i++) {
// Serial.print("SV ID: ");
// Serial.println(gsvData.svInfo[i].svID);
// Serial.print("Elevation: ");
// Serial.println(gsvData.svInfo[i].elevation);
// Serial.print("Azimuth: ");
// Serial.println(gsvData.svInfo[i].azimuth);
// Serial.print("SNR: ");
// Serial.println(gsvData.svInfo[i].snr);
// }
} else if (nmea.startsWith("$NAVACC")) {
// Serial.print("NAVACC Time: ");
// Serial.println(navaccData.time);
// Serial.print("NAVACC Status: ");
// Serial.println(navaccData.status);
// Serial.print("Horizontal Accuracy: ");
// Serial.println(navaccData.pAcc);
// Serial.print("Vertical Accuracy: ");
// Serial.println(navaccData.vAcc);
// Serial.print("Ground Course Accuracy: ");
// Serial.println(navaccData.cAcc);
}
}
vTaskDelay(1000 / portTICK_PERIOD_MS); // Delay to yield control
}
}

81
src/init.cpp Normal file
View File

@ -0,0 +1,81 @@
/*
___ _ ___ _
| _ \ ( )_ | _ \ (_ )
| (_) ) _ | _) _ _ __ | (_) ) __ _ _ | | ___ ___
| / / _ \| | / _ \( __) | / / __ \/ _ )| |/ _ _ \
| |\ \( (_) ) |_( (_) ) | | |\ \( ___/ (_| || || ( ) ( ) |
(_) (_)\___/ \__)\___/(_) (_) (_)\____)\__ _)___)_) (_) (_)
* This file is part of Rotor Realm RemoteID
*
* Copyright (c) 2024 Rotor Realm
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <FS.h>
#include <SPIFFS.h>
#include <ArduinoJson.h>
#include "init.h"
Config config;
void loadConfig() {
if (!SPIFFS.begin(true)) {
Serial.println("An error has occurred while mounting SPIFFS");
return;
}
File configFile = SPIFFS.open("/config.json", "r");
if (!configFile) {
Serial.println("Failed to open config file");
return;
}
size_t size = configFile.size();
if (size > 1024) {
Serial.println("Config file size is too large");
return;
}
std::unique_ptr<char[]> buf(new char[size]);
configFile.readBytes(buf.get(), size);
DynamicJsonDocument doc(1024);
auto error = deserializeJson(doc, buf.get());
if (error) {
Serial.print(F("Failed to parse config file: "));
Serial.println(error.c_str());
return;
}
config.enableBT = doc["config"]["enableBT"];
config.enableWiFi = doc["config"]["enableWiFi"];
config.operatorInfo.OperatorID = doc["operator"]["OperatorID"].as<String>();
config.operatorInfo.OperatorLatitude = doc["operator"]["OperatorLatitude"];
config.operatorInfo.OperatorLongitude = doc["operator"]["OperatorLongitude"];
config.operatorInfo.OperatorTimezone = doc["operator"]["OperatorTimezone"].as<String>();
Serial.println("Config loaded successfully");
Serial.println("enableBT: " + String(config.enableBT));
Serial.println("enableWiFi: " + String(config.enableWiFi));
Serial.println("OperatorID: " + config.operatorInfo.OperatorID);
Serial.println("OperatorLatitude: " + String(config.operatorInfo.OperatorLatitude));
Serial.println("OperatorLongitude: " + String(config.operatorInfo.OperatorLongitude));
Serial.println("OperatorTimezone: " + config.operatorInfo.OperatorTimezone);
}

131
src/main.cpp Normal file
View File

@ -0,0 +1,131 @@
/*
___ _ ___ _
| _ \ ( )_ | _ \ (_ )
| (_) ) _ | _) _ _ __ | (_) ) __ _ _ | | ___ ___
| / / _ \| | / _ \( __) | / / __ \/ _ )| |/ _ _ \
| |\ \( (_) ) |_( (_) ) | | |\ \( ___/ (_| || || ( ) ( ) |
(_) (_)\___/ \__)\___/(_) (_) (_)\____)\__ _)___)_) (_) (_)
* This file is part of Rotor Realm RemoteID
*
* Copyright (c) 2024 Rotor Realm
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "Arduino.h"
#include "gps.h"
#include "display.h"
#include "wifiBeacon.h"
#include "init.h"
#include <freertos/FreeRTOS.h>
#include <freertos/task.h>
#define VGNSS_CTRL 3
String ssidString;
const char* ssid;
TaskHandle_t wifiBeaconTaskHandle = NULL; // Declare the task handle globally
void setup() {
ssidString = generateUASID();
ssid = ssidString.c_str();
pinMode(VGNSS_CTRL, OUTPUT);
digitalWrite(VGNSS_CTRL, HIGH);
Serial.begin(115200);
// Order of Operations
loadConfig();
gpsSetup();
displaySetup(ssid);
wifiBeaconSetup(ssid);
Serial.println("Creating GPS Task...");
if (xTaskCreate(
gpsTask, // Function to be called
"GPS Task", // Name of the task
8192, // Stack size (increased size)
NULL, // Task input parameter
1, // Priority of the task
NULL // Task handle
)
!= pdPASS) {
Serial.println("Failed to create GPS Task");
} else {
Serial.println("GPS Task created successfully");
}
Serial.println("Creating WiFi Beacon Task...");
if (xTaskCreatePinnedToCore(
wifiBeaconTask, // Function to be called
"WiFi Beacon Task", // Name of the task
8192, // Stack size (increased size)
(void*)ssid, // Task input parameter
2, // Priority of the task
&wifiBeaconTaskHandle, // Task handle
0 // Core number (0 or 1 for ESP32)
)
!= pdPASS) {
Serial.println("Failed to create WiFi Beacon Task");
} else {
Serial.println("WiFi Beacon Task created successfully");
vTaskSuspend(wifiBeaconTaskHandle); // Suspend the task immediately
}
Serial.println("Creating Display Task...");
if (xTaskCreate(
displayTask, // Function to be called
"Display Task", // Name of the task
8192, // Stack size (increased size)
NULL, // Task input parameter
3, // Priority of the task
NULL // Task handle
)
!= pdPASS) {
Serial.println("Failed to create Display Task");
} else {
Serial.println("Display Task created successfully");
}
}
void loop() {
if (ggaData.numSatellites > 1) {
vTaskResume(wifiBeaconTaskHandle); // Resume the task if the condition is met
} else {
vTaskSuspend(wifiBeaconTaskHandle); // Suspend the task if the condition is not met
}
vTaskDelay(1000 / portTICK_PERIOD_MS); // Delay to prevent rapid suspend/resume calls
}
String generateUASID() {
String prefix = "RRDIF";
uint64_t chipId = ESP.getEfuseMac();
String serialNumber = String(chipId, HEX);
serialNumber.toUpperCase();
while (serialNumber.length() < 7) {
serialNumber = "0" + serialNumber;
}
String uasID = prefix + serialNumber;
while (uasID.length() < 19) {
uasID += "0";
}
return uasID;
}

264
src/nema.cpp Normal file
View File

@ -0,0 +1,264 @@
/*
___ _ ___ _
| _ \ ( )_ | _ \ (_ )
| (_) ) _ | _) _ _ __ | (_) ) __ _ _ | | ___ ___
| / / _ \| | / _ \( __) | / / __ \/ _ )| |/ _ _ \
| |\ \( (_) ) |_( (_) ) | | |\ \( ___/ (_| || || ( ) ( ) |
(_) (_)\___/ \__)\___/(_) (_) (_)\____)\__ _)___)_) (_) (_)
* This file is part of Rotor Realm RemoteID
*
* Copyright (c) 2024 Rotor Realm
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "nema.h"
double convertToDecimalDegrees(double coordinate, char direction) {
int degrees = int(coordinate / 100);
double minutes = coordinate - (degrees * 100);
double decimalDegrees = degrees + (minutes / 60.0);
if (direction == 'S' || direction == 'W') {
decimalDegrees = -decimalDegrees;
}
return decimalDegrees;
}
double parseDouble(String value) {
return value.toFloat();
}
int parseInt(String value) {
return value.toInt();
}
void parseGGA(String gga, GGAData &ggaData) {
int idx = 0;
int fieldIndex = 0;
String field;
while (idx < gga.length()) {
int endIdx = gga.indexOf(',', idx);
if (endIdx == -1) endIdx = gga.length();
field = gga.substring(idx, endIdx);
switch (fieldIndex) {
case 1: // UTC Time
ggaData.time = field;
break;
case 2: // Latitude
ggaData.latitude = parseDouble(field);
break;
case 3: // Latitude Direction
ggaData.latDir = field.charAt(0);
break;
case 4: // Longitude
ggaData.longitude = parseDouble(field);
break;
case 5: // Longitude Direction
ggaData.lonDir = field.charAt(0);
break;
case 6: // Fix Quality
ggaData.fixQuality = parseInt(field);
break;
case 7: // Number of Satellites
ggaData.numSatellites = parseInt(field);
break;
case 8: // HDOP
ggaData.hdop = parseDouble(field);
break;
case 9: // Altitude
ggaData.altitude = parseDouble(field);
break;
case 10: // Altitude Unit
ggaData.altUnit = field.charAt(0);
break;
case 11: // Geoid Height
ggaData.geoidHeight = parseDouble(field);
break;
case 12: // Geoid Unit
ggaData.geoUnit = field.charAt(0);
break;
case 13: // DGPS Age
ggaData.dgpsAge = parseDouble(field);
break;
case 14: // DGPS Station ID
ggaData.dgpsStationId = parseInt(field);
break;
}
idx = endIdx + 1;
fieldIndex++;
}
// Convert latitude and longitude to proper format
ggaData.latitude = convertToDecimalDegrees(ggaData.latitude, ggaData.latDir);
ggaData.longitude = convertToDecimalDegrees(ggaData.longitude, ggaData.lonDir);
}
void parseRMC(String rmc, RMCData &rmcData) {
int idx = 0;
int fieldIndex = 0;
String field;
while (idx < rmc.length()) {
int endIdx = rmc.indexOf(',', idx);
if (endIdx == -1) endIdx = rmc.length();
field = rmc.substring(idx, endIdx);
switch (fieldIndex) {
case 1: // UTC Time
rmcData.time = field;
break;
case 2: // Status
rmcData.status = field.charAt(0);
break;
case 3: // Latitude
rmcData.latitude = parseDouble(field);
break;
case 4: // Latitude Direction
rmcData.latDir = field.charAt(0);
break;
case 5: // Longitude
rmcData.longitude = parseDouble(field);
break;
case 6: // Longitude Direction
rmcData.lonDir = field.charAt(0);
break;
case 7: // Speed
rmcData.speed = parseDouble(field);
break;
case 8: // Course
rmcData.course = parseDouble(field);
break;
case 9: // Date
rmcData.date = field;
break;
case 10: // Magnetic Variation
rmcData.magVar = parseDouble(field);
break;
case 11: // Magnetic Variation Direction
rmcData.magVarDir = field.charAt(0);
break;
case 12: // Mode
rmcData.mode = field.charAt(0);
break;
}
idx = endIdx + 1;
fieldIndex++;
}
// Convert latitude and longitude to proper format
rmcData.latitude = convertToDecimalDegrees(rmcData.latitude, rmcData.latDir);
rmcData.longitude = convertToDecimalDegrees(rmcData.longitude, rmcData.lonDir);
}
void parseGSA(String gsa, GSAData &gsaData) {
int idx = 0;
int endIdx = gsa.indexOf(',', idx);
gsaData.mode1 = gsa.charAt(idx);
idx = endIdx + 1;
endIdx = gsa.indexOf(',', idx);
gsaData.mode2 = parseInt(gsa.substring(idx, endIdx));
for (int i = 0; i < 12; i++) {
idx = endIdx + 1;
endIdx = gsa.indexOf(',', idx);
gsaData.sv[i] = parseInt(gsa.substring(idx, endIdx));
}
idx = endIdx + 1;
endIdx = gsa.indexOf(',', idx);
gsaData.pdop = parseDouble(gsa.substring(idx, endIdx));
idx = endIdx + 1;
endIdx = gsa.indexOf(',', idx);
gsaData.hdop = parseDouble(gsa.substring(idx, endIdx));
idx = endIdx + 1;
gsaData.vdop = parseDouble(gsa.substring(idx));
}
void parseGSV(String gsv, GSVData &gsvData) {
int idx = 0;
int endIdx = gsv.indexOf(',', idx);
gsvData.numMessages = parseInt(gsv.substring(idx, endIdx));
idx = endIdx + 1;
endIdx = gsv.indexOf(',', idx);
gsvData.messageNum = parseInt(gsv.substring(idx, endIdx));
idx = endIdx + 1;
endIdx = gsv.indexOf(',', idx);
gsvData.numSVs = parseInt(gsv.substring(idx, endIdx));
for (int i = 0; i < 4; i++) {
idx = endIdx + 1;
endIdx = gsv.indexOf(',', idx);
gsvData.svInfo[i].svID = parseInt(gsv.substring(idx, endIdx));
idx = endIdx + 1;
endIdx = gsv.indexOf(',', idx);
gsvData.svInfo[i].elevation = parseInt(gsv.substring(idx, endIdx));
idx = endIdx + 1;
endIdx = gsv.indexOf(',', idx);
gsvData.svInfo[i].azimuth = parseInt(gsv.substring(idx, endIdx));
idx = endIdx + 1;
endIdx = gsv.indexOf(',', idx);
gsvData.svInfo[i].snr = parseInt(gsv.substring(idx, endIdx));
}
}
void parseNAVACC(String nmea, NAVACCData &navaccData) {
int idx = nmea.indexOf(',');
int endIdx = nmea.indexOf(',', idx + 1);
navaccData.pAcc = nmea.substring(idx + 1, endIdx).toInt();
idx = endIdx;
endIdx = nmea.indexOf(',', idx + 1);
navaccData.vAcc = nmea.substring(idx + 1, endIdx).toInt();
idx = endIdx;
endIdx = nmea.indexOf(',', idx + 1);
navaccData.cAcc = nmea.substring(idx + 1, endIdx).toInt();
}
void parseNMEA(String nmea, GGAData &ggaData, RMCData &rmcData, GSAData &gsaData, GSVData &gsvData, NAVACCData &navaccData) {
if (nmea.startsWith("$GPGGA") || nmea.startsWith("$GBGGA") || nmea.startsWith("$GAGGA") || nmea.startsWith("$GLGGA") || nmea.startsWith("$GNGGA")) {
parseGGA(nmea, ggaData);
} else if (nmea.startsWith("$GPRMC") || nmea.startsWith("$GBRMC") || nmea.startsWith("$GARMC") || nmea.startsWith("$GLRMC") || nmea.startsWith("$GNRMC")) {
parseRMC(nmea, rmcData);
} else if (nmea.startsWith("$GPGSA") || nmea.startsWith("$GBGSA") || nmea.startsWith("$GAGSA") || nmea.startsWith("$GLGSA") || nmea.startsWith("$GNGSA")) {
parseGSA(nmea, gsaData);
} else if (nmea.startsWith("$GPGSV") || nmea.startsWith("$GBGSV") || nmea.startsWith("$GAGSV") || nmea.startsWith("$GLGSV") || nmea.startsWith("$GNGSV")) {
parseGSV(nmea, gsvData);
} else if (nmea.startsWith("$NAVACC")) {
parseNAVACC(nmea, navaccData);
}
}

203
src/wifiBeacon.cpp Normal file
View File

@ -0,0 +1,203 @@
/*
___ _ ___ _
| _ \ ( )_ | _ \ (_ )
| (_) ) _ | _) _ _ __ | (_) ) __ _ _ | | ___ ___
| / / _ \| | / _ \( __) | / / __ \/ _ )| |/ _ _ \
| |\ \( (_) ) |_( (_) ) | | |\ \( ___/ (_| || || ( ) ( ) |
(_) (_)\___/ \__)\___/(_) (_) (_)\____)\__ _)___)_) (_) (_)
* This file is part of Rotor Realm RemoteID
*
* Copyright (c) 2024 Rotor Realm
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include "wifiBeacon.h"
#include "init.h" // Include init.h to access the config variable
#include "gps.h" // Include gps.h to access the GPS data
#include <WiFi.h>
#include <esp_wifi.h>
// Define the WiFi channel and output power
const int wifiChannel = 6; // Set WiFi Channel
const int wifiOutputPower = 20; // dBm
// const int ledPin = LED_BUILTIN;
// WiFi packet buffer
uint8_t beaconPacket[256];
uint8_t packetIndex = 0;
static uint8_t beaconCounter = 0; // Static variable to keep track of how many times the beacon has been sent
extern Config config; // Access the global config variable
extern GGAData ggaData; // Access the GPS GGA data
extern RMCData rmcData;
extern NAVACCData navaccData; // Access the GPS NAVACC data
void wifiBeaconSetup(const char* ssid) {
pinMode(LED, OUTPUT);
Serial.begin(115200);
WiFi.mode(WIFI_MODE_STA);
esp_wifi_set_promiscuous(true);
esp_wifi_set_channel(wifiChannel, WIFI_SECOND_CHAN_NONE);
int channel = wifiChannel;
int beaconInterval = 100;
constructBeaconFrame(ssid, channel, beaconInterval);
}
void wifiBeaconTask(void* parameters) {
for (;;) {
esp_wifi_80211_tx(WIFI_IF_STA, beaconPacket, packetIndex, false);
beaconCounter++;
// digitalWrite(LED, HIGH);
analogWrite(LED, 96);
constructBeaconFrame((const char*)parameters, wifiChannel, 100);
// digitalWrite(LED, LOW);
analogWrite(LED, 0);
vTaskDelay(1000 / portTICK_PERIOD_MS);
}
}
void constructBeaconFrame(const char* ssid, int channel, int beaconInterval) {
packetIndex = 0;
beaconPacket[packetIndex++] = 0x80;
beaconPacket[packetIndex++] = 0x00;
beaconPacket[packetIndex++] = 0x00;
beaconPacket[packetIndex++] = 0x00;
memset(&beaconPacket[packetIndex], 0xFF, 6);
packetIndex += 6;
uint8_t sourceAddress[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xDE, 0xAD };
memcpy(&beaconPacket[packetIndex], sourceAddress, 6);
packetIndex += 6;
memcpy(&beaconPacket[packetIndex], sourceAddress, 6);
packetIndex += 6;
beaconPacket[packetIndex++] = 0x00;
beaconPacket[packetIndex++] = 0x00;
memset(&beaconPacket[packetIndex], 0x00, 8);
packetIndex += 8;
beaconPacket[packetIndex++] = beaconInterval & 0xFF;
beaconPacket[packetIndex++] = (beaconInterval >> 8) & 0xFF;
beaconPacket[packetIndex++] = 0x01;
beaconPacket[packetIndex++] = 0x00;
beaconPacket[packetIndex++] = 0x00;
uint8_t ssidLength = strlen(ssid);
beaconPacket[packetIndex++] = ssidLength;
memcpy(&beaconPacket[packetIndex], ssid, ssidLength);
packetIndex += ssidLength;
beaconPacket[packetIndex++] = 0x01;
beaconPacket[packetIndex++] = 0x08;
beaconPacket[packetIndex++] = 0x82;
beaconPacket[packetIndex++] = 0x84;
beaconPacket[packetIndex++] = 0x8B;
beaconPacket[packetIndex++] = 0x96;
beaconPacket[packetIndex++] = 0x0C;
beaconPacket[packetIndex++] = 0x12;
beaconPacket[packetIndex++] = 0x18;
beaconPacket[packetIndex++] = 0x24;
beaconPacket[packetIndex++] = 0x03;
beaconPacket[packetIndex++] = 0x01;
beaconPacket[packetIndex++] = channel;
encodeVendorSpecificElement(ssid);
}
void encodeVendorSpecificElement(const char* ssid) {
VendorSpecificTag vendorTag;
MessagePack messagePack;
messagePack.messageSize = 25;
messagePack.numMessages = 5;
// Basic ID Message
BasicIDMessage basicIDMessage;
encodeBasicIDMessage(
&basicIDMessage, // Basic ID message struct
idSerial, // UAS ID Type
uaMultirotor, // UAS Type
ssid // UAS ID (SSID)
);
memcpy(messagePack.messages[0], &basicIDMessage, sizeof(BasicIDMessage));
// Location Message
LocationMessage locationMessage;
encodeLocationMessage(&locationMessage,
osAirborne, // Status
static_cast<uint8_t>(rmcData.course), // Track Direction
static_cast<uint8_t>(rmcData.speed), // Speed
0, // Vertical Speed (can be replaced with actual value if available)
ggaData.latitude, // Latitude
ggaData.longitude, // Longitude
ggaData.altitude, // Pressure Altitude (float)
ggaData.geoidHeight, // Geodetic Altitude (float)
static_cast<int16_t>(ggaData.altitude), // Height
navaccData.pAcc, // Horizontal Accuracy
navaccData.vAcc, // Vertical Accuracy
navaccData.cAcc, // Speed/Baro Accuracy
ggaData.time.toInt() // Timestamp
); // Timestamp
memcpy(messagePack.messages[1], &locationMessage, sizeof(LocationMessage));
// Self ID Message
SelfIDMessage selfIDMessage;
encodeSelfIDMessage(
&selfIDMessage, // Self ID message struct
selfIdType, // Self ID Type
selfIdDescription // Self ID Description
);
memcpy(messagePack.messages[2], &selfIDMessage, sizeof(SelfIDMessage));
// System Message
SystemMessage systemMessage;
uint8_t uaClass = 0;
encodeSystemMessage(
&systemMessage, // System message struct
0, // Operator Location Type
config.operatorInfo.OperatorLatitude, // Operator Latitude
config.operatorInfo.OperatorLongitude, // Operator Longitude
1, // Area Count
0, // Area Radius
0, // Area Ceiling
0, // Area Floor
cfUndeclared, // Classification Type
uaMultirotor, // UA Category
uaClass, // UA Class
0, // Operator Altitude
0 // Timestamp
);
memcpy(messagePack.messages[3], &systemMessage, sizeof(SystemMessage));
// Operator ID Message
OperatorIDMessage operatorIDMessage;
encodeOperatorIDMessage(
&operatorIDMessage, // Operator ID message struct
0, // Operator ID Type
config.operatorInfo.OperatorID.c_str() // Operator ID
);
memcpy(messagePack.messages[4], &operatorIDMessage, sizeof(OperatorIDMessage));
encodeMessageHeader(&messagePack.header, msMessagePack, 0x01);
encodeVendorSpecificTag(&vendorTag, &messagePack);
beaconPacket[packetIndex++] = 0xDD;
beaconPacket[packetIndex++] = vendorTag.length;
memcpy(&beaconPacket[packetIndex], vendorTag.oui, 3);
packetIndex += 3;
beaconPacket[packetIndex++] = vendorTag.vendType;
beaconPacket[packetIndex++] = vendorTag.msgCounter;
memcpy(&beaconPacket[packetIndex], vendorTag.advData, vendorTag.length - 5);
packetIndex += vendorTag.length - 5;
}

11
test/README Normal file
View File

@ -0,0 +1,11 @@
This directory is intended for PlatformIO Test Runner and project tests.
Unit Testing is a software testing method by which individual units of
source code, sets of one or more MCU program modules together with associated
control data, usage procedures, and operating procedures, are tested to
determine whether they are fit for use. Unit testing finds problems early
in the development cycle.
More information about PlatformIO Unit Testing:
- https://docs.platformio.org/en/latest/advanced/unit-testing/index.html