kopia lustrzana https://github.com/lightaprs/LightAPRS-1.0
Create LightAPRS-vehicle.ino
rodzic
5f4115ea17
commit
337ecb5a5d
|
@ -0,0 +1,541 @@
|
|||
#include <LibAPRS.h> //Modified version of https://github.com/markqvist/LibAPRS
|
||||
#include <SoftwareSerial.h>
|
||||
#include <TinyGPS++.h> //https://github.com/mikalhart/TinyGPSPlus
|
||||
#include <LowPower.h> //https://github.com/rocketscream/Low-Power
|
||||
#include <Wire.h>
|
||||
#include <Adafruit_BMP085.h>//https://github.com/adafruit/Adafruit-BMP085-Library
|
||||
#include <avr/wdt.h>
|
||||
|
||||
#define RfPDPin 19
|
||||
#define GpsVccPin 18
|
||||
#define RfPwrHLPin 21
|
||||
#define RfPttPin 20
|
||||
#define BattPin A2
|
||||
#define PIN_DRA_RX 22
|
||||
#define PIN_DRA_TX 23
|
||||
|
||||
#define ADC_REFERENCE REF_3V3
|
||||
#define OPEN_SQUELCH false
|
||||
|
||||
#define GpsON digitalWrite(GpsVccPin, LOW)//PNP
|
||||
#define GpsOFF digitalWrite(GpsVccPin, HIGH)
|
||||
#define RfON digitalWrite(RfPDPin, HIGH)
|
||||
#define RfOFF digitalWrite(RfPDPin, LOW)
|
||||
#define RfPwrHigh pinMode(RfPwrHLPin, INPUT)
|
||||
#define RfPwrLow pinMode(RfPwrHLPin, OUTPUT);digitalWrite(RfPwrHLPin, LOW)
|
||||
#define RfPttON digitalWrite(RfPttPin, HIGH)//NPN
|
||||
#define RfPttOFF digitalWrite(RfPttPin, LOW)
|
||||
#define AprsPinInput pinMode(12,INPUT);pinMode(13,INPUT);pinMode(14,INPUT);pinMode(15,INPUT)
|
||||
#define AprsPinOutput pinMode(12,OUTPUT);pinMode(13,OUTPUT);pinMode(14,OUTPUT);pinMode(15,OUTPUT)
|
||||
|
||||
//#define DEVMODE // Development mode. Uncomment to enable for debugging.
|
||||
|
||||
//****************************************************************************
|
||||
char CallSign[7]="NOCALL"; //DO NOT FORGET TO CHANGE YOUR CALLSIGN
|
||||
int CallNumber=9; //SSID http://www.aprs.org/aprs11/SSIDs.txt
|
||||
char Symbol='>'; // '/>' for car, '/k' for truck, for more info : http://www.aprs.org/symbols/symbols-new.txt
|
||||
bool alternateSymbolTable = false ; //false = '/' , true = '\'
|
||||
|
||||
char Frequency[9]="144.3900"; //default frequency. 144.3900 for US, 144.8000 for Europe
|
||||
|
||||
char comment[50] = "http://www.lightaprs.com"; // Max 50 char
|
||||
char StatusMessage[50] = "LightAPRS by TA9OHC & TA2MUN";
|
||||
//*****************************************************************************
|
||||
|
||||
|
||||
unsigned int BeaconWait=60; //seconds sleep for next beacon (TX).
|
||||
unsigned int BattWait=60; //seconds sleep if super capacitors/batteries are below BattMin (important if power source is solar panel)
|
||||
float BattMin=4.5; // min Volts to wake up.
|
||||
float DraHihgVolt=4.8; // min Volts for radio module (DRA818V) to transmit (TX) 1 Watt, below this transmit 0.5 Watt.
|
||||
float GpsMinVolt=4.0; //min Volts for GPS to wake up. (important if power source is solar panel)
|
||||
|
||||
boolean aliveStatus = true; //for tx status message on first wake-up just once.
|
||||
|
||||
//do not change WIDE path settings below if you don't know what you are doing :)
|
||||
byte Wide1=1; // 1 for WIDE1-1 path
|
||||
byte Wide2=1; // 1 for WIDE2-1 path
|
||||
|
||||
/**
|
||||
Airborne stations above a few thousand feet should ideally use NO path at all, or at the maximum just WIDE2-1 alone.
|
||||
Due to their extended transmit range due to elevation, multiple digipeater hops are not required by airborne stations.
|
||||
Multi-hop paths just add needless congestion on the shared APRS channel in areas hundreds of miles away from the aircraft's own location.
|
||||
NEVER use WIDE1-1 in an airborne path, since this can potentially trigger hundreds of home stations simultaneously over a radius of 150-200 miles.
|
||||
*/
|
||||
int pathSize=2; // 2 for WIDE1-N,WIDE2-N ; 1 for WIDE2-N
|
||||
boolean autoPathSizeHighAlt = true; //force path to WIDE2-N only for high altitude (airborne) beaconing (over 1.000 meters (3.280 feet))
|
||||
|
||||
boolean GpsFirstFix=false;
|
||||
|
||||
static char telemetry_buff[100];// telemetry buffer
|
||||
uint16_t TxCount = 1;
|
||||
|
||||
TinyGPSPlus gps;
|
||||
Adafruit_BMP085 bmp;
|
||||
String serialCommand;
|
||||
|
||||
|
||||
void setup() {
|
||||
wdt_enable(WDTO_8S);
|
||||
analogReference(INTERNAL2V56);
|
||||
pinMode(RfPDPin, OUTPUT);
|
||||
pinMode(GpsVccPin, OUTPUT);
|
||||
pinMode(RfPwrHLPin, OUTPUT);
|
||||
pinMode(RfPttPin, OUTPUT);
|
||||
pinMode(BattPin, INPUT);
|
||||
pinMode(PIN_DRA_TX,INPUT);
|
||||
|
||||
RfOFF;
|
||||
GpsOFF;
|
||||
RfPwrLow;
|
||||
RfPttOFF;
|
||||
|
||||
Serial.begin(57600);
|
||||
Serial1.begin(9600);
|
||||
#if defined(DEVMODE)
|
||||
Serial.println(F("Start"));
|
||||
#endif
|
||||
|
||||
APRS_init(ADC_REFERENCE, OPEN_SQUELCH);
|
||||
APRS_setCallsign(CallSign,CallNumber);
|
||||
APRS_setDestination("APLIGA", 0);
|
||||
APRS_setMessageDestination("APLIGA", 0);
|
||||
APRS_setPath1("WIDE1", Wide1);
|
||||
APRS_setPath2("WIDE2", Wide2);
|
||||
APRS_useAlternateSymbolTable(alternateSymbolTable);
|
||||
APRS_setSymbol(Symbol);
|
||||
APRS_setPathSize(pathSize);
|
||||
AprsPinInput;
|
||||
|
||||
configDra818(Frequency);
|
||||
|
||||
bmp.begin();
|
||||
|
||||
}
|
||||
|
||||
void loop() {
|
||||
wdt_reset();
|
||||
|
||||
if (readBatt() > BattMin) {
|
||||
|
||||
|
||||
if(aliveStatus){
|
||||
|
||||
//send status tx on startup once (before gps fix)
|
||||
|
||||
#if defined(DEVMODE)
|
||||
Serial.println(F("Sending"));
|
||||
#endif
|
||||
sendStatus();
|
||||
#if defined(DEVMODE)
|
||||
Serial.println(F("Sent"));
|
||||
#endif
|
||||
|
||||
aliveStatus = false;
|
||||
|
||||
}
|
||||
|
||||
updateGpsData(1000);
|
||||
gpsDebug();
|
||||
|
||||
|
||||
if ((gps.location.age() < 1000 || gps.location.isUpdated()) && gps.location.isValid()) {
|
||||
if (gps.satellites.isValid() && (gps.satellites.value() > 3)) {
|
||||
updatePosition();
|
||||
updateTelemetry();
|
||||
|
||||
if((gps.satellites.value() > 7)) {
|
||||
GpsOFF;
|
||||
}
|
||||
|
||||
GpsFirstFix=true;
|
||||
|
||||
if(autoPathSizeHighAlt && gps.altitude.feet()>10000){
|
||||
//force to use high altitude settings (WIDE2-n)
|
||||
APRS_setPathSize(1);
|
||||
} else {
|
||||
//use defualt settings
|
||||
APRS_setPathSize(pathSize);
|
||||
}
|
||||
|
||||
//send status message every 60 minutes
|
||||
if(gps.time.minute() == 30){
|
||||
sendStatus();
|
||||
} else {
|
||||
|
||||
sendLocation();
|
||||
|
||||
}
|
||||
|
||||
freeMem();
|
||||
Serial.flush();
|
||||
sleepSeconds(BeaconWait);
|
||||
|
||||
} else {
|
||||
#if defined(DEVMODE)
|
||||
Serial.println(F("Not enough sattelites"));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
sleepSeconds(BattWait);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void aprs_msg_callback(struct AX25Msg *msg) {
|
||||
//do not remove this function, necessary for LibAPRS
|
||||
}
|
||||
|
||||
void sleepSeconds(int sec) {
|
||||
if(GpsFirstFix)GpsOFF;//sleep gps after first fix
|
||||
RfOFF;
|
||||
RfPttOFF;
|
||||
Serial.flush();
|
||||
wdt_disable();
|
||||
for (int i = 0; i < sec; i++) {
|
||||
if(readBatt() < GpsMinVolt) GpsOFF; //(for pico balloon only)
|
||||
LowPower.powerDown(SLEEP_1S, ADC_OFF, BOD_ON);
|
||||
}
|
||||
wdt_enable(WDTO_8S);
|
||||
}
|
||||
|
||||
|
||||
|
||||
byte configDra818(char *freq)
|
||||
{
|
||||
SoftwareSerial Serial_dra(PIN_DRA_RX, PIN_DRA_TX);
|
||||
Serial_dra.begin(9600);
|
||||
RfON;
|
||||
char ack[3];
|
||||
int n;
|
||||
delay(2000);
|
||||
char cmd[50];
|
||||
sprintf(cmd, "AT+DMOSETGROUP=0,%s,%s,0000,4,0000", freq, freq);
|
||||
Serial_dra.println(cmd);
|
||||
ack[2] = 0;
|
||||
while (ack[2] != 0xa)
|
||||
{
|
||||
if (Serial_dra.available() > 0) {
|
||||
ack[0] = ack[1];
|
||||
ack[1] = ack[2];
|
||||
ack[2] = Serial_dra.read();
|
||||
}
|
||||
}
|
||||
Serial_dra.end();
|
||||
RfOFF;
|
||||
pinMode(PIN_DRA_TX,INPUT);
|
||||
#if defined(DEVMODE)
|
||||
if (ack[0] == 0x30) Serial.println(F("Frequency updated...")); else Serial.println(F("Frequency update error!"));
|
||||
#endif
|
||||
return (ack[0] == 0x30) ? 1 : 0;
|
||||
}
|
||||
|
||||
void updatePosition() {
|
||||
// Convert and set latitude NMEA string Degree Minute Hundreths of minutes ddmm.hh[S,N].
|
||||
char latStr[10];
|
||||
int temp = 0;
|
||||
|
||||
double d_lat = gps.location.lat();
|
||||
double dm_lat = 0.0;
|
||||
|
||||
if (d_lat < 0.0) {
|
||||
temp = -(int)d_lat;
|
||||
dm_lat = temp * 100.0 - (d_lat + temp) * 60.0;
|
||||
} else {
|
||||
temp = (int)d_lat;
|
||||
dm_lat = temp * 100 + (d_lat - temp) * 60.0;
|
||||
}
|
||||
|
||||
dtostrf(dm_lat, 7, 2, latStr);
|
||||
|
||||
if (dm_lat < 1000) {
|
||||
latStr[0] = '0';
|
||||
}
|
||||
|
||||
if (d_lat >= 0.0) {
|
||||
latStr[7] = 'N';
|
||||
} else {
|
||||
latStr[7] = 'S';
|
||||
}
|
||||
|
||||
APRS_setLat(latStr);
|
||||
|
||||
// Convert and set longitude NMEA string Degree Minute Hundreths of minutes ddmm.hh[E,W].
|
||||
char lonStr[10];
|
||||
double d_lon = gps.location.lng();
|
||||
double dm_lon = 0.0;
|
||||
|
||||
if (d_lon < 0.0) {
|
||||
temp = -(int)d_lon;
|
||||
dm_lon = temp * 100.0 - (d_lon + temp) * 60.0;
|
||||
} else {
|
||||
temp = (int)d_lon;
|
||||
dm_lon = temp * 100 + (d_lon - temp) * 60.0;
|
||||
}
|
||||
|
||||
dtostrf(dm_lon, 8, 2, lonStr);
|
||||
|
||||
if (dm_lon < 10000) {
|
||||
lonStr[0] = '0';
|
||||
}
|
||||
if (dm_lon < 1000) {
|
||||
lonStr[1] = '0';
|
||||
}
|
||||
|
||||
if (d_lon >= 0.0) {
|
||||
lonStr[8] = 'E';
|
||||
} else {
|
||||
lonStr[8] = 'W';
|
||||
}
|
||||
|
||||
APRS_setLon(lonStr);
|
||||
}
|
||||
|
||||
|
||||
void updateTelemetry() {
|
||||
|
||||
sprintf(telemetry_buff, "%03d", gps.course.isValid() ? (int)gps.course.deg() : 0);
|
||||
telemetry_buff[3] += '/';
|
||||
sprintf(telemetry_buff + 4, "%03d", gps.speed.isValid() ? (int)gps.speed.knots() : 0);
|
||||
telemetry_buff[7] = '/';
|
||||
telemetry_buff[8] = 'A';
|
||||
telemetry_buff[9] = '=';
|
||||
sprintf(telemetry_buff + 10, "%06d", (long)gps.altitude.feet());
|
||||
telemetry_buff[16] = ' ';
|
||||
sprintf(telemetry_buff + 17, "%03d", TxCount);
|
||||
telemetry_buff[20] = 'T';
|
||||
telemetry_buff[21] = 'x';
|
||||
telemetry_buff[22] = 'C';
|
||||
telemetry_buff[23] = ' '; float tempC = bmp.readTemperature();//-21.4;//
|
||||
dtostrf(tempC, 6, 2, telemetry_buff + 24);
|
||||
telemetry_buff[30] = 'C';
|
||||
telemetry_buff[31] = ' '; float pressure = bmp.readPressure() / 100.0; //Pa to hPa
|
||||
dtostrf(pressure, 7, 2, telemetry_buff + 32);
|
||||
telemetry_buff[39] = 'h';
|
||||
telemetry_buff[40] = 'P';
|
||||
telemetry_buff[41] = 'a';
|
||||
telemetry_buff[42] = ' ';
|
||||
dtostrf(readBatt(), 5, 2, telemetry_buff + 43);
|
||||
telemetry_buff[48] = 'V';
|
||||
telemetry_buff[49] = ' ';
|
||||
sprintf(telemetry_buff + 50, "%02d", gps.satellites.isValid() ? (int)gps.satellites.value() : 0);
|
||||
telemetry_buff[52] = 'S';
|
||||
telemetry_buff[53] = ' ';
|
||||
sprintf(telemetry_buff + 54, "%s", comment);
|
||||
|
||||
|
||||
#if defined(DEVMODE)
|
||||
Serial.println(telemetry_buff);
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
void sendLocation() {
|
||||
|
||||
#if defined(DEVMODE)
|
||||
Serial.println(F("Location sending with comment"));
|
||||
#endif
|
||||
if (readBatt() > DraHihgVolt) RfPwrHigh; //DRA Power 1 Watt
|
||||
else RfPwrLow; //DRA Power 0.5 Watt
|
||||
|
||||
int hh = gps.time.hour();
|
||||
int mm = gps.time.minute();
|
||||
int ss = gps.time.second();
|
||||
|
||||
char timestamp_buff[7];
|
||||
|
||||
sprintf(timestamp_buff, "%02d", gps.time.isValid() ? (int)gps.time.hour() : 0);
|
||||
sprintf(timestamp_buff + 2, "%02d", gps.time.isValid() ? (int)gps.time.minute() : 0);
|
||||
sprintf(timestamp_buff + 4, "%02d", gps.time.isValid() ? (int)gps.time.second() : 0);
|
||||
timestamp_buff[6] = 'h';
|
||||
AprsPinOutput;
|
||||
RfON;
|
||||
delay(2000);
|
||||
RfPttON;
|
||||
delay(1000);
|
||||
|
||||
APRS_sendLocWtTmStmp(telemetry_buff, strlen(telemetry_buff), timestamp_buff); //beacon with timestamp
|
||||
delay(50);
|
||||
while(digitalRead(1)){;}//LibAprs TX Led pin PB1
|
||||
delay(50);
|
||||
RfPttOFF;
|
||||
RfOFF;
|
||||
AprsPinInput;
|
||||
#if defined(DEVMODE)
|
||||
Serial.println(F("Location sent with comment"));
|
||||
#endif
|
||||
|
||||
TxCount++;
|
||||
}
|
||||
|
||||
void sendStatus() {
|
||||
if (readBatt() > DraHihgVolt) RfPwrHigh; //DRA Power 1 Watt
|
||||
else RfPwrLow; //DRA Power 0.5 Watt
|
||||
AprsPinOutput;
|
||||
RfON;
|
||||
delay(2000);
|
||||
RfPttON;
|
||||
delay(1000);
|
||||
|
||||
APRS_sendStatus(StatusMessage, strlen(StatusMessage));
|
||||
delay(50);
|
||||
while(digitalRead(1)){;}//LibAprs TX Led pin PB1
|
||||
delay(50);
|
||||
RfPttOFF;
|
||||
RfOFF;
|
||||
AprsPinInput;
|
||||
#if defined(DEVMODE)
|
||||
Serial.println(F("Status sent"));
|
||||
#endif
|
||||
|
||||
TxCount++;
|
||||
|
||||
}
|
||||
|
||||
|
||||
static void updateGpsData(int ms)
|
||||
{
|
||||
GpsON;
|
||||
while (!Serial1) {
|
||||
delayMicroseconds(1); // wait for serial port to connect.
|
||||
}
|
||||
unsigned long start = millis();
|
||||
unsigned long bekle=0;
|
||||
do
|
||||
{
|
||||
while (Serial1.available()>0) {
|
||||
char c;
|
||||
c=Serial1.read();
|
||||
gps.encode(c);
|
||||
bekle= millis();
|
||||
}
|
||||
if (bekle!=0 && bekle+10<millis())break;
|
||||
} while (millis() - start < ms);
|
||||
|
||||
}
|
||||
|
||||
float readBatt() {
|
||||
float R1 = 560000.0; // 560K
|
||||
float R2 = 100000.0; // 100K
|
||||
float value = 0.0;
|
||||
do {
|
||||
value =analogRead(BattPin);
|
||||
delay(5);
|
||||
value =analogRead(BattPin);
|
||||
value=value-8;
|
||||
value = (value * 2.56) / 1024.0;
|
||||
value = value / (R2/(R1+R2));
|
||||
} while (value > 16.0);
|
||||
return value ;
|
||||
}
|
||||
|
||||
void freeMem() {
|
||||
#if defined(DEVMODE)
|
||||
Serial.print(F("Free RAM: ")); Serial.print(freeMemory()); Serial.println(F(" byte"));
|
||||
#endif
|
||||
}
|
||||
|
||||
void gpsDebug() {
|
||||
#if defined(DEVMODE)
|
||||
Serial.println();
|
||||
Serial.println(F("Sats HDOP Latitude Longitude Fix Date Time Date Alt Course Speed Card Chars Sentences Checksum"));
|
||||
Serial.println(F(" (deg) (deg) Age Age (m) --- from GPS ---- RX RX Fail"));
|
||||
Serial.println(F("-----------------------------------------------------------------------------------------------------------------"));
|
||||
|
||||
printInt(gps.satellites.value(), gps.satellites.isValid(), 5);
|
||||
printInt(gps.hdop.value(), gps.hdop.isValid(), 5);
|
||||
printFloat(gps.location.lat(), gps.location.isValid(), 11, 6);
|
||||
printFloat(gps.location.lng(), gps.location.isValid(), 12, 6);
|
||||
printInt(gps.location.age(), gps.location.isValid(), 5);
|
||||
printDateTime(gps.date, gps.time);
|
||||
printFloat(gps.altitude.meters(), gps.altitude.isValid(), 7, 2);
|
||||
printFloat(gps.course.deg(), gps.course.isValid(), 7, 2);
|
||||
printFloat(gps.speed.kmph(), gps.speed.isValid(), 6, 2);
|
||||
printStr(gps.course.isValid() ? TinyGPSPlus::cardinal(gps.course.value()) : "*** ", 6);
|
||||
|
||||
printInt(gps.charsProcessed(), true, 6);
|
||||
printInt(gps.sentencesWithFix(), true, 10);
|
||||
printInt(gps.failedChecksum(), true, 9);
|
||||
Serial.println();
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static void printFloat(float val, bool valid, int len, int prec)
|
||||
{
|
||||
#if defined(DEVMODE)
|
||||
if (!valid)
|
||||
{
|
||||
while (len-- > 1)
|
||||
Serial.print('*');
|
||||
Serial.print(' ');
|
||||
}
|
||||
else
|
||||
{
|
||||
Serial.print(val, prec);
|
||||
int vi = abs((int)val);
|
||||
int flen = prec + (val < 0.0 ? 2 : 1); // . and -
|
||||
flen += vi >= 1000 ? 4 : vi >= 100 ? 3 : vi >= 10 ? 2 : 1;
|
||||
for (int i = flen; i < len; ++i)
|
||||
Serial.print(' ');
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void printInt(unsigned long val, bool valid, int len)
|
||||
{
|
||||
#if defined(DEVMODE)
|
||||
char sz[32] = "*****************";
|
||||
if (valid)
|
||||
sprintf(sz, "%ld", val);
|
||||
sz[len] = 0;
|
||||
for (int i = strlen(sz); i < len; ++i)
|
||||
sz[i] = ' ';
|
||||
if (len > 0)
|
||||
sz[len - 1] = ' ';
|
||||
Serial.print(sz);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void printDateTime(TinyGPSDate &d, TinyGPSTime &t)
|
||||
{
|
||||
#if defined(DEVMODE)
|
||||
if (!d.isValid())
|
||||
{
|
||||
Serial.print(F("********** "));
|
||||
}
|
||||
else
|
||||
{
|
||||
char sz[32];
|
||||
sprintf(sz, "%02d/%02d/%02d ", d.month(), d.day(), d.year());
|
||||
Serial.print(sz);
|
||||
}
|
||||
|
||||
if (!t.isValid())
|
||||
{
|
||||
Serial.print(F("******** "));
|
||||
}
|
||||
else
|
||||
{
|
||||
char sz[32];
|
||||
sprintf(sz, "%02d:%02d:%02d ", t.hour(), t.minute(), t.second());
|
||||
Serial.print(sz);
|
||||
}
|
||||
|
||||
printInt(d.age(), d.isValid(), 5);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void printStr(const char *str, int len)
|
||||
{
|
||||
#if defined(DEVMODE)
|
||||
int slen = strlen(str);
|
||||
for (int i = 0; i < len; ++i)
|
||||
Serial.print(i < slen ? str[i] : ' ');
|
||||
#endif
|
||||
}
|
||||
|
||||
|
Ładowanie…
Reference in New Issue