#include "UltimateGPS.hpp"
#include <nmea.h>
#include <nmea/gpgga.h>
#include <nmea/gprmc.h>
#include <nmea/gpgsv.h>
#include <algorithm>
#define SEND_TO_LORAWAN
uint8_t fixQuality = 0;
#ifdef SEND_TO_LORAWAN
#include <LoRaMacKR920.hpp>
static uint8_t devEui[] = "\x98\x76\xB6\x00\x00\x10\xED\x7D";
static const uint8_t appEui[] = "\x00\x00\x00\x00\x00\x00\x00\x00";
static uint8_t appKey[] = "\x94\x62\xef\xdb\x8c\x62\x01\xe7\x52\x09\x8f\x17\x3c\x2a\x4b\x1c";
struct timeval tLoRaWANDisplayed;
static void taskPeriodicSend(void *) {
if (!f) {
printf("* Out of memory\n");
return;
}
if (fixQuality) {
"\"gnss\":[%lf,%lf]",
latToReport.
degrees + latToReport.
minutes / 60,
longToReport.degrees + longToReport.minutes / 60
);
} else {
f->len = sprintf((char *) f->buf, "\"gnss\":0");
}
error_t err = LoRaWAN.send(f);
display.clearDisplay();
display.setCursor(0, 0);
display.print("Uplink ");
display.print(f->len);
display.print(' ');
display.println("byte");
display.print(" - Result:");
display.print(err);
display.display();
gettimeofday(&tLoRaWANDisplayed, nullptr);
Serial0.printf(
"* Sending periodic report (%s (%u byte)): %d\n", f->buf, f->len, err);
delete f;
return;
}
err = LoRaWAN.requestDeviceTime();
printf("* Request DeviceTime: %d\n", err);
}
#endif
void setup() {
display.display();
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(
WHITE);
display.setCursor(0, 0);
display.println("*** [Feather-M0] ***");
display.println("Ultimate GPS Test");
display.display();
gps.begin();
gps.turnOn();
gps.onNMEAReceived = [](
Gps&) {
struct timeval tNow, tDiff;
gettimeofday(&tNow, nullptr);
timersub(&tNow, &tLoRaWANDisplayed, &tDiff);
bool doDisplay = (tDiff.tv_sec >= 2);
if (doDisplay) {
display.clearDisplay();
display.setCursor(0, 0);
}
char *data;
if (gps.getLatestGGA() != nullptr) {
data = new char[strlen(gps.getLatestGGA()) + 1];
if (data) {
strcpy(data, gps.getLatestGGA());
parsed = nmea_parse(data, strlen(data), 0);
if (parsed && parsed->
type == NMEA_GPGGA) {
nmea_gpgga_s *gga = (nmea_gpgga_s *) parsed;
Serial0.printf(
" Position fix indicator:%u\n", gga->position_fix);
Serial0.printf(
" Time: %02u:%02u:%02u\n", gga->time.tm_hour, gga->time.tm_min, gga->time.tm_sec);
Serial0.printf(
" Longitude: %d deg %lf %c\n", gga->longitude.degrees, gga->longitude.minutes, gga->longitude.cardinal);
Serial0.printf(
" Latitude: %d deg %lf %c\n", gga->latitude.degrees, gga->latitude.minutes, gga->latitude.cardinal);
Serial0.printf(
" Altitude: %d %c\n", gga->altitude, gga->altitude_unit);
Serial0.printf(
" Number of satellites: %d\n", gga->n_satellites);
latToReport = gga->latitude;
longToReport = gga->longitude;
fixQuality = gga->position_fix;
if (doDisplay) {
if (gga->time.tm_hour < 10) display.print('0');
display.print(gga->time.tm_hour);
display.print(':');
if (gga->time.tm_min < 10) display.print('0');
display.print(gga->time.tm_min);
display.print(':');
if (gga->time.tm_sec < 10) display.print('0');
display.print(gga->time.tm_sec);
display.print(" ");
int32_t vBat =
map(analogRead(D9), 0, 4095, 0, 2 * 3300 * 100 / 148);
display.print(vBat);
display.println(" mV");
if (gga->position_fix == 0) {
display.println("Position not fixed.");
display.print("Wait");
static uint8_t dot_wait = 0;
switch (dot_wait++ % 3) {
case 0: display.print(". "); break;
case 1: display.print(" . "); break;
default: display.print(" ."); break;
}
} else {
display.print(gga->latitude.degrees);
display.print(" deg ");
display.print(gga->latitude.minutes, 4);
display.print(' ');
display.println(gga->latitude.cardinal);
display.print(gga->longitude.degrees);
display.print(" deg ");
display.print(gga->longitude.minutes, 4);
display.print(' ');
display.println(gga->longitude.cardinal);
display.print(gga->altitude);
display.print(' ');
display.println(gga->altitude_unit);
}
display.display();
}
} else {
Serial0.printf(
" Parsing GGA error (%p)\n", parsed);
}
delete[] data;
} else {
Serial0.printf(
" No memory to parse GGA\n");
}
} else {
}
if (parsed) {
nmea_free(parsed);
}
for (uint8_t i = 0; i < 3; i++) {
if (gps.getLatestGSV(i) != nullptr) {
data = new char[strlen(gps.getLatestGSV(i)) + 1];
if (data) {
strcpy(data, gps.getLatestGSV(i));
parsed = nmea_parse(data, strlen(data), 0);
if (parsed && parsed->type == NMEA_GPGSV) {
nmea_gpgsv_s *gsv = (nmea_gpgsv_s *) parsed;
Serial0.printf(
" Sentence number:%u\n", gsv->sentence_number);
Serial0.printf(
" Number of satellites:%u\n", gsv->satellites);
for (uint8_t i = 0; i < 4; i++) {
if (gsv->sat[i].prn >= 0) {
"PRN:%d, elevation:%d, azimuth:%d, snr:%d\n",
gsv->sat[i].prn, gsv->sat[i].elevation, gsv->sat[i].azimuth, gsv->sat[i].snr
);
} else {
}
}
} else {
Serial0.printf(
" Parsing GSV error (%p)\n", parsed);
}
delete[] data;
} else {
Serial0.printf(
" No memory to parse GSV\n");
}
} else {
Serial0.printf(
"- GSV[%u]: (null)\n", i);
}
if (parsed) {
nmea_free(parsed);
}
}
if (gps.getLatestRMC() != nullptr) {
data = new char[strlen(gps.getLatestRMC()) + 1];
if (data) {
strcpy(data, gps.getLatestRMC());
parsed = nmea_parse(data, strlen(data), 0);
if (parsed && parsed->type == NMEA_GPRMC) {
nmea_gprmc_s *rmc = (nmea_gprmc_s *) parsed;
Serial0.printf(
" Status: %s\n", rmc->valid ?
"valid" :
"not valid");
" Date time: %u-%02u-%02u %02u:%02u:%02u\n",
rmc->date_time.tm_year + 1900,
rmc->date_time.tm_mon + 1,
rmc->date_time.tm_mday,
rmc->date_time.tm_hour,
rmc->date_time.tm_min,
rmc->date_time.tm_sec
);
Serial0.printf(
" Longitude: %d deg %lf %c\n", rmc->longitude.degrees, rmc->longitude.minutes, rmc->longitude.cardinal);
Serial0.printf(
" Latitude: %d deg %lf %c\n", rmc->latitude.degrees, rmc->latitude.minutes, rmc->latitude.cardinal);
Serial0.printf(
" Speed: %lf\n", rmc->gndspd_knots);
} else {
Serial0.printf(
" Parsing RMC error (%p)\n", parsed);
}
delete[] data;
} else {
Serial0.printf(
" No memory to parse RMC\n");
}
} else {
}
if (parsed) {
nmea_free(parsed);
}
Serial0.printf(
"GSA: %s\n", (gps.getLatestGSA()) ? gps.getLatestGSA() :
"(null)");
};
attachInterrupt(D5, []() {
display.clearDisplay();
display.setCursor(0, 0);
if (gps.isOn()) {
display.print("Turn off GPS !");
gps.turnOff();
} else {
display.print("Turn on GPS !");
gps.turnOn();
}
display.display();
}, FALLING);
#ifdef SEND_TO_LORAWAN
System.setTimeDiff(9 * 60);
RFM95.antennaGain = -6;
LoRaWAN.begin([]() -> uint8_t {
int32_t vBat =
map(analogRead(D9), 0, 4095, 0, 2 * 3300 * 100 / 148);
if (vBat < 0) {
return 255;
} else {
return map(vBat, 3300, 4200, 1, 254);
}
});
display.clearDisplay();
display.setCursor(0, 0);
display.print("Uplink (");
display.print(frame->
fCnt);
display.println(") done");
display.print(" - Result:");
display.display();
gettimeofday(&tLoRaWANDisplayed, nullptr);
"* Send done(%d): destined for port:%u, fCnt:0x%lX, Freq:%lu Hz, "
"Power:%d dBm, # of Tx:%u, ",
);
const char *strBW[] = {
"Unknown", "125kHz", "250kHz", "500kHz", "Unexpected value"
};
}
);
} else {
Serial0.printf(
"Unkndown modulation, ");
}
Serial0.printf(
"MULTICAST (error)");
} else {
}
for (uint8_t t = 0; t < frame->
numTrials; t++) {
const char *strTxResult[] = {
"not started",
"success",
"no ack",
"air busy",
"Tx timeout",
};
Serial0.printf(
"- [%u] %s\n", t, strTxResult[std::min((
int) frame->
txResult[t], 4)]);
}
delete frame;
});
LoRaWAN.onJoin([](
bool joined,
const uint8_t *joinedDevEui,
const uint8_t *joinedAppEui,
const uint8_t *joinedAppKey,
const uint8_t *joinedNwkSKey,
const uint8_t *joinedAppSKey,
uint32_t joinedDevAddr,
uint32_t airTime
) {
Serial0.printf(
"* Tx time of JoinRequest: %lu usec.\n", airTime);
if (joined) {
Serial0.println(
"* Joining done!");
} else {
Serial0.println(
"* Joining failed. Retry to join.");
}
});
LoRaWAN.onDeviceTimeAnswered(
[](
bool success,
uint32_t tSeconds,
uint8_t tFracSeconds
) {
if (success) {
struct tm tLocal, tUtc;
System.getDateTime(tLocal);
System.getUTC(tUtc);
printf(
"* LoRaWAN DeviceTime answered: (%lu + %u/256) GPS epoch.\n"
"- Adjusted local time: %u-%u-%u %02u:%02u:%02u\n"
"- Adjusted UTC time: %u-%u-%u %02u:%02u:%02u\n",
tSeconds, tFracSeconds,
tLocal.tm_year + 1900, tLocal.tm_mon + 1, tLocal.tm_mday,
tLocal.tm_hour, tLocal.tm_min, tLocal.tm_sec,
tUtc.tm_year + 1900, tUtc.tm_mon + 1, tUtc.tm_mday,
tUtc.tm_hour, tUtc.tm_min, tUtc.tm_sec
);
} else {
printf("* LoRaWAN DeviceTime not answered\n");
}
},
&System
);
LoRaWAN.setPublicNetwork(false);
"* DevEUI: %02X-%02X-%02X-%02X-%02X-%02X-%02X-%02X\n",
devEui[0], devEui[1], devEui[2], devEui[3],
devEui[4], devEui[5], devEui[6], devEui[7]
);
"* AppKey: %02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X\n",
appKey[0], appKey[1], appKey[2], appKey[3],
appKey[4], appKey[5], appKey[6], appKey[7],
appKey[8], appKey[9], appKey[10], appKey[11],
appKey[12], appKey[13], appKey[14], appKey[15]
);
Serial0.println(
"* Let's start join!");
LoRaWAN.beginJoining(devEui, appEui, appKey);
timerSend.
onFired(taskPeriodicSend,
nullptr);
#endif
}
Class that stores state and functions for interacting with SSD1306 OLED displays.
Definition Adafruit_SSD1306.hpp:123
Common GPS callback interface and NMEA parsers for device-specific drivers.
Definition GPS.hpp:50
LoRaWAN frame.
Definition LoRaMac.hpp:88
TxResult_t txResult[8]
Tx 시도(attempt) 별 Tx 결과
Definition LoRaMac.hpp:135
FrameType_t type
Frame type.
Definition LoRaMac.hpp:102
Radio::LoRaSF_t sf
송신시 사용할, 또는 수신시 사용된 spreading factor
Definition LoRaMac.hpp:189
int16_t power
송신시 사용할 Tx power table index (LoRaMac::MaxTxPower ~ LoRaMac::MinTxPower), 송신 완료 콜백시 출력 세기(단위: dBm) 또는...
Definition LoRaMac.hpp:146
Radio::LoRaBW_t bw
송신시 사용할, 또는 수신시 사용된 bandwidth
Definition LoRaMac.hpp:206
struct LoRaMacFrame::@024007217170142272131231362111253110374345035353::@215311014042340047327077224045033277162306213223 LoRa
modulation 이 Radio::MOD_LORA 인 경우 사용되는 변수
uint8_t numTrials
전송 횟수
Definition LoRaMac.hpp:116
uint32_t fCnt
Frame counter.
Definition LoRaMac.hpp:121
union LoRaMacFrame::@024007217170142272131231362111253110374345035353 meta
변복조 방식에 대한 속성
@ MULTICAST
Definition LoRaMac.hpp:95
@ PROPRIETARY
Definition LoRaMac.hpp:96
@ UNCONFIRMED
Definition LoRaMac.hpp:93
@ CONFIRMED
Definition LoRaMac.hpp:94
uint8_t port
Port number.
Definition LoRaMac.hpp:107
대한민국 920~923MHz 대역을 사용하는 LoRaMac
Definition LoRaMacKR920.hpp:25
LoRaWAN 단말 장치들을 위한 MAC 프로토콜
Definition LoRaMac.hpp:253
virtual error_t beginJoining(const uint8_t *devEui, const uint8_t *appEui, const uint8_t *appKey)
Over-the-Air Activation (OTAA) 방식의 join을 시도합니다.
무선 패킷
Definition RadioPacket.hpp:59
Radio::Modulation_t modulation
송신시 사용할, 또는 수신시 사용된 변복조 방식
Definition RadioPacket.hpp:160
uint32_t freq
송신시 사용할, 또는 수신시 사용된 주파수 (단위: Hz)
Definition RadioPacket.hpp:143
Result_t result
송신 또는 수신 패킷 처리 결과
Definition RadioPacket.hpp:131
Adafruit Feather M0 RFM9x LoRa Radio.
Definition System.hpp:292
Timer는 일정 시간 이후에 1회 실행될 작업, 매 일정 시간마다 실행될 작업 등을 위한 기능입니다.
Definition Timer.hpp:61
bool startOneShot(uint32_t millis, bool interrupt=false)
작업함수를 millis 밀리초 이후 1회 실행합니다.
void onFired(void(*func)(void *), void *args=nullptr)
타이머 만료시 실행시킬 작업함수를 지정합니다.
long map(long x, long in_min, long in_max, long out_min, long out_max)
수를 어떤 범위에서 다른 범위로 매핑시킵니다.
void delay(uint16_t msec)
msec 밀리초를 지연시킵니다.
@ ERROR_SUCCESS
Definition nola-common.h:119
bool digitalWrite(int8_t pin, int value)
pin 를 HIGH 또는 LOW 로 설정합니다.
bool pinMode(int8_t pin, enum ioDirection direction)
pin 을 direction 방향을 갖는 디지털 핀으로 동작하도록 설정합니다.
@ OUTPUT
Definition nola-common.h:383
@ INPUT_PULLUP
Definition nola-common.h:384
@ INPUT
Definition nola-common.h:382
#define WHITE
Draw 'on' pixels.
Definition Adafruit_SSD1306.hpp:66
#define SSD1306_SWITCHCAPVCC
Gen. display voltage from 3.3V.
Definition Adafruit_SSD1306.hpp:95
LoRaBW_t
LoRa Bandwidth Type.
Definition Radio.hpp:107
@ MOD_FSK
Definition Radio.hpp:67
@ MOD_LORA
Definition Radio.hpp:68