Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions examples/companion_radio/ui-new/UITask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -308,22 +308,37 @@ class HomeScreen : public UIScreen {
display.drawTextLeftAlign(0, y, buf);
if (nmea == NULL) {
y = y + 12;
display.drawTextLeftAlign(0, y, "Can't access GPS");
display.drawTextLeftAlign(0, y, "No GPS detected");
} else {
strcpy(buf, nmea->isValid()?"fix":"no fix");
if (!gps_state) {
strcpy(buf, "off");
} else switch (nmea->getFixType()) {
case 3: strcpy(buf, "3D"); break;
case 2: strcpy(buf, "2D"); break;
default: strcpy(buf, "no fix"); break;
}
display.drawTextRightAlign(display.width()-1, y, buf);
y = y + 12;
display.drawTextLeftAlign(0, y, "sat");
sprintf(buf, "%d", nmea->satellitesCount());
display.drawTextRightAlign(display.width()-1, y, buf);
y = y + 12;
bool has_pos = (nmea->getLatitude() != 0 || nmea->getLongitude() != 0);
display.drawTextLeftAlign(0, y, "pos");
sprintf(buf, "%.4f %.4f",
nmea->getLatitude()/1000000., nmea->getLongitude()/1000000.);
if (has_pos) {
sprintf(buf, "%.4f %.4f",
nmea->getLatitude()/1000000., nmea->getLongitude()/1000000.);
} else {
strcpy(buf, "---");
}
display.drawTextRightAlign(display.width()-1, y, buf);
y = y + 12;
display.drawTextLeftAlign(0, y, "alt");
sprintf(buf, "%.2f", nmea->getAltitude()/1000.);
if (has_pos) {
sprintf(buf, "%.2f", nmea->getAltitude()/1000.);
} else {
strcpy(buf, "---");
}
display.drawTextRightAlign(display.width()-1, y, buf);
y = y + 12;
}
Expand Down
2 changes: 1 addition & 1 deletion examples/companion_radio/ui-tiny/UITask.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,7 @@ class HomeScreen : public UIScreen {
display.drawTextLeftAlign(0, y, buf);
if (nmea == NULL) {
// y = y + 8;
display.drawTextLeftAlign(0, y, "Can't access GPS");
display.drawTextLeftAlign(0, y, "No GPS detected");
} else {
if (!gps_state || !nmea->isValid()) {
strcpy(buf, "no fix");
Expand Down
145 changes: 118 additions & 27 deletions src/helpers/sensors/EnvironmentSensorManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,9 @@ static RAK12035_SoilMoisture RAK12035;
#endif

#ifdef RAK_WISBLOCK_GPS
static uint32_t gpsResetPin = 0;
// -1 = no enable pin; out-of-range values are no-ops in pinMode/digitalWrite,
// while 0 would be a real GPIO (P0.00 = LFXO crystal on nRF52)
static uint32_t gpsResetPin = -1;
static bool i2cGPSFlag = false;
static bool serialGPSFlag = false;
#ifndef TELEM_RAK12500_ADDRESS
Expand All @@ -181,28 +183,71 @@ class RAK12500LocationProvider : public LocationProvider {
int _sats = 0;
long _epoch = 0;
bool _fix = false;
uint8_t _fixType = 0;
unsigned long _next_poll = 0;
public:
long getLatitude() override { return _lat; }
long getLongitude() override { return _lng; }
long getAltitude() override { return _alt; }
long satellitesCount() override { return _sats; }
bool isValid() override { return _fix; }
uint8_t getFixType() override { return _fixType; }
long getTimestamp() override { return _epoch; }
void sendSentence(const char * sentence) override { }
void reset() override { }
void begin() override { }
void stop() override { }
void loop() override {
if (ublox_GNSS.getGnssFixOk(8)) {
_fix = true;
_lat = ublox_GNSS.getLatitude(2) / 10;
_lng = ublox_GNSS.getLongitude(2) / 10;
_alt = ublox_GNSS.getAltitude(2);
_sats = ublox_GNSS.getSIV(2);
// Throttle the I²C poll to ~1 Hz. The getXxx() calls below each issue a
// synchronous ublox poll that blocks up to its maxWait; the GNSS solution only
// updates at the 1 Hz navigation rate (setMeasurementRate(1000)), so polling on
// every main-loop iteration just burns the loop on redundant I²C round-trips —
// that stall is what made the AIN1 button sluggish and drop double-clicks.
// Polling once per nav epoch keeps the fix/time fresh while leaving the loop
// free for button sampling the rest of the time.
if (millis() < _next_poll) {
return;
}
_next_poll = millis() + 1000;

// The numeric arg is maxWait (ms) for the I²C poll, not a field index.
// The previous 2/8 ms values were below the ublox response window, so polls
// routinely timed out and returned stale/garbage PVT data (observed: random
// wrong-hemisphere longitudes despite getGnssFixOk() returning true).
// 250 ms is a ceiling; a responsive module returns well before that. getPVT()
// does the single poll here, then every getter reads that same cached packet
// (passing no maxWait, so they never re-poll). Gating all reads on getPVT()
// succeeding matters: if the poll times out, a getter with a stale cache would
// otherwise fire its own poll at the library default (~1100 ms) and stall hard.
if (!ublox_GNSS.getPVT(250)) {
return; // no response this tick — keep last reported values
}

// Show the position EAGERLY: as soon as there's a real 2D/3D solution (SIV>0,
// fixType>=2) with valid coords, display it -- don't wait for gnssFixOk, which can
// take up to ~30 s to assert. Coords must be present (0/0 is the "no data" value) and
// in range: the module emits 0/0 and ~999deg junk while cold/re-tracking, so rejecting
// those keeps _lat/_lng at 0 (-> "---"). Telemetry is stricter: _fix (= isValid()) only
// goes true once gnssFixOk passes on a full 3D fix, and node_lat is gated on isValid()
// -- so the screen can show a settling fix while we withhold a loose one from the mesh.
_sats = ublox_GNSS.getSIV(); // valid without a fix -- shows acquisition progress
long lat = ublox_GNSS.getLatitude() / 10;
long lng = ublox_GNSS.getLongitude() / 10;
uint8_t ft = (_sats > 0) ? ublox_GNSS.getFixType() : 0;
bool coords_ok = (lat != 0 || lng != 0) &&
lat >= -90000000 && lat <= 90000000 && // +/-90 deg (1e6 scale)
lng >= -180000000 && lng <= 180000000; // +/-180 deg
if (ft >= 2 && coords_ok) {
_fixType = ft; // 2 or 3 -- shown on screen with the coords
_lat = lat;
_lng = lng;
_alt = ublox_GNSS.getAltitudeMSL(); // height above mean sea level, not ellipsoid
_fix = (ft == 3 && ublox_GNSS.getGnssFixOk()); // isValid()/telemetry: accuracy-OK 3D only
} else {
_fixType = 0; // no usable fix -> "no fix" + held/--- position
_fix = false;
}
_epoch = ublox_GNSS.getUnixEpoch(2);
_epoch = ublox_GNSS.getUnixEpoch();
}
bool isEnabled() override { return true; }
};
Expand Down Expand Up @@ -787,16 +832,25 @@ void EnvironmentSensorManager::rakGPSInit(){
//search for the correct IO standby pin depending on socket used
if(gpsIsAwake(WB_IO2)){
}
else if(gpsIsAwake(WB_IO4)){
}
else if(gpsIsAwake(WB_IO5)){
}
else{
MESH_DEBUG_PRINTLN("No GPS found");
gps_active = false;
gps_detected = false;
Serial1.end();
return;
else {
// WB_IO2 controls the 3V3_S switched peripheral rail on these RAK boards.
// gpsIsAwake() leaves the pin as INPUT on failure, which drops the rail and
// takes I²C peripherals (RTC, display, the GPS itself) down with it.
// Restore it before probing other sockets.
pinMode(WB_IO2, OUTPUT);
digitalWrite(WB_IO2, HIGH);

if(gpsIsAwake(WB_IO4)){
}
else if(gpsIsAwake(WB_IO5)){
}
else{
MESH_DEBUG_PRINTLN("No GPS found");
gps_active = false;
gps_detected = false;
Serial1.end();
return;
}
}

#ifndef FORCE_GPS_ALIVE // for use with repeaters, until GPS toggle is implimented
Expand Down Expand Up @@ -837,7 +891,7 @@ bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){
} else if (Serial1.available()) {
MESH_DEBUG_PRINTLN("Serial GPS init correctly and is turned on");
#ifdef PIN_GPS_EN
if(PIN_GPS_EN){
if(PIN_GPS_EN >= 0){
gpsResetPin = PIN_GPS_EN;
}
#endif
Expand All @@ -856,8 +910,23 @@ bool EnvironmentSensorManager::gpsIsAwake(uint8_t ioPin){
void EnvironmentSensorManager::start_gps() {
gps_active = true;
#ifdef RAK_WISBLOCK_GPS
pinMode(gpsResetPin, OUTPUT);
digitalWrite(gpsResetPin, HIGH);
#if defined(PIN_GPS_EN) && (PIN_GPS_EN >= 0)
pinMode(gpsResetPin, OUTPUT);
digitalWrite(gpsResetPin, HIGH); // dedicated GPS enable pin
#else
// No dedicated enable pin: gpsResetPin is the shared 3V3_S rail (WB_IO2),
// which also powers the RTC/boost and must stay HIGH. Wake the receiver in
// software instead of touching the rail.
if (i2cGPSFlag) {
// UBX-CFG-RST "controlled GNSS start": resume the receiver stopped below.
static const uint8_t ubxGnssStart[] = {0xB5,0x62,0x06,0x04,0x04,0x00,0x00,0x00,0x09,0x00,0x17,0x76};
Wire.beginTransmission((uint8_t)TELEM_RAK12500_ADDRESS);
Wire.write(ubxGnssStart, sizeof(ubxGnssStart));
Wire.endTransmission();
} else if (serialGPSFlag) {
Serial1.write((uint8_t)0xFF); // wake MTK/Quectel from standby
}
#endif
return;
#endif

Expand All @@ -872,8 +941,25 @@ void EnvironmentSensorManager::start_gps() {
void EnvironmentSensorManager::stop_gps() {
gps_active = false;
#ifdef RAK_WISBLOCK_GPS
pinMode(gpsResetPin, OUTPUT);
digitalWrite(gpsResetPin, LOW);
#if defined(PIN_GPS_EN) && (PIN_GPS_EN >= 0)
pinMode(gpsResetPin, OUTPUT);
digitalWrite(gpsResetPin, LOW); // dedicated GPS enable pin
#else
// No dedicated enable pin: gpsResetPin is the shared 3V3_S rail (WB_IO2),
// which also powers the RTC/boost and must stay HIGH. Don't drop it; put
// the receiver into software low-power instead (rail/RTC stay up, warm-start).
if (i2cGPSFlag) {
// UBX-CFG-RST "controlled GNSS stop": stops RF/tracking but keeps the host
// interface alive, so it restarts reliably over I2C. (PMREQ backup only wakes
// on EXTINT/UART, not I2C -- confirmed dead on this hardware.)
static const uint8_t ubxGnssStop[] = {0xB5,0x62,0x06,0x04,0x04,0x00,0x00,0x00,0x08,0x00,0x16,0x74};
Wire.beginTransmission((uint8_t)TELEM_RAK12500_ADDRESS);
Wire.write(ubxGnssStop, sizeof(ubxGnssStop));
Wire.endTransmission();
} else if (serialGPSFlag) {
Serial1.println("$PMTK161,0*28"); // Quectel/MTK standby
}
#endif
return;
#endif

Expand All @@ -890,6 +976,7 @@ void EnvironmentSensorManager::loop() {

#if ENV_INCLUDE_GPS
static long next_gps_update = 0;
static uint8_t gps_debug_skip = 0; // throttle: print roughly every 10th update tick
if (gps_active) {
_location->loop();
}
Expand All @@ -900,17 +987,21 @@ void EnvironmentSensorManager::loop() {
if ((i2cGPSFlag || serialGPSFlag) && _location->isValid()) {
node_lat = ((double)_location->getLatitude())/1000000.;
node_lon = ((double)_location->getLongitude())/1000000.;
MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon);
node_altitude = ((double)_location->getAltitude()) / 1000.0;
MESH_DEBUG_PRINTLN("lat %f lon %f alt %f", node_lat, node_lon, node_altitude);
if (gps_debug_skip-- == 0) {
gps_debug_skip = 9;
MESH_DEBUG_PRINTLN("lat %f lon %f alt %f", node_lat, node_lon, node_altitude);
}
}
#else
if (_location->isValid()) {
node_lat = ((double)_location->getLatitude())/1000000.;
node_lon = ((double)_location->getLongitude())/1000000.;
MESH_DEBUG_PRINTLN("lat %f lon %f", node_lat, node_lon);
node_altitude = ((double)_location->getAltitude()) / 1000.0;
MESH_DEBUG_PRINTLN("lat %f lon %f alt %f", node_lat, node_lon, node_altitude);
if (gps_debug_skip-- == 0) {
gps_debug_skip = 9;
MESH_DEBUG_PRINTLN("lat %f lon %f alt %f", node_lat, node_lon, node_altitude);
}
}
#endif
}
Expand Down
4 changes: 3 additions & 1 deletion src/helpers/sensors/EnvironmentSensorManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,9 @@ class EnvironmentSensorManager : public SensorManager {
public:
#if ENV_INCLUDE_GPS
EnvironmentSensorManager(LocationProvider &location): _location(&location){};
LocationProvider* getLocationProvider() { return _location; }
// NULL when no GPS was detected, so the UI shows "No GPS detected" (CLI: "gps provider
// not found") instead of an unfixed-but-present GPS. All consumers already null-check.
LocationProvider* getLocationProvider() { return gps_detected ? _location : NULL; }
#else
EnvironmentSensorManager(){};
#endif
Expand Down
1 change: 1 addition & 0 deletions src/helpers/sensors/LocationProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ class LocationProvider {
virtual long getAltitude() = 0;
virtual long satellitesCount() = 0;
virtual bool isValid() = 0;
virtual uint8_t getFixType() { return isValid() ? 3 : 0; } // 0=none, 2=2D, 3=3D; providers may override
virtual long getTimestamp() = 0;
virtual void sendSentence(const char * sentence);
virtual void reset() = 0;
Expand Down
11 changes: 8 additions & 3 deletions src/helpers/sensors/MicroNMEALocationProvider.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,15 +112,20 @@ public :
}

void syncTime() override { nmea.clear(); LocationProvider::syncTime(); }
long getLatitude() override { return nmea.getLatitude(); }
long getLongitude() override { return nmea.getLongitude(); }
// MicroNMEA reports lat/lng = 999000000 (999 deg) when there is no fix; map that to 0
// (the codebase's no-data value) so the UI shows "---" and node_lat stays 0.
long getLatitude() override { long v = nmea.getLatitude(); return v == 999000000L ? 0 : v; }
long getLongitude() override { long v = nmea.getLongitude(); return v == 999000000L ? 0 : v; }
long getAltitude() override {
long alt = 0;
nmea.getAltitude(alt);
return alt;
}
long satellitesCount() override { return nmea.getNumSatellites(); }
bool isValid() override { return nmea.isValid(); }
// Require actual coordinates, not just the RMC 'A' status flag: some MTK/Quectel
// receivers assert 'A' for seconds before the position is computed -- and MicroNMEA
// reports lat==999000000 until then. Without this it'd show "fix" with no coords.
bool isValid() override { return nmea.isValid() && nmea.getLatitude() != 999000000L; }

long getTimestamp() override {
DateTime dt(nmea.getYear(), nmea.getMonth(),nmea.getDay(),nmea.getHour(),nmea.getMinute(),nmea.getSecond());
Expand Down
4 changes: 4 additions & 0 deletions variants/rak3401/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,17 @@ build_flags = ${nrf52_base.build_flags}
${sensor_base.build_flags}
-I variants/rak3401
-D RAK_3401
-D RAK_BOARD
-D NRF52_POWER_MANAGEMENT
-D RADIO_CLASS=CustomSX1262
-D WRAPPER_CLASS=CustomSX1262Wrapper
-D LORA_TX_POWER=22
-D SX126X_CURRENT_LIMIT=140
-D SX126X_RX_BOOSTED_GAIN=1
-D SX126X_REGISTER_PATCH=1 ; Patch register 0x8B5 for improved RX with SKY66122 FEM
-D FORCE_GPS_ALIVE ; on RAK3401, gpsResetPin ends up = WB_IO2 (kills 3V3_S rail) or
; pin 4 (= SX126X_RESET, holds radio in reset). stop_gps() must not run.
-D PIN_GPS_EN=-1 ; -1 = no dedicated GPS enable pin (power is the shared 3V3_S rail), same as rak4631
build_src_filter = ${nrf52_base.build_src_filter}
+<../variants/rak3401>
+<helpers/sensors>
Expand Down
4 changes: 2 additions & 2 deletions variants/rak3401/variant.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,8 +188,8 @@ static const uint8_t AREF = PIN_AREF;
// Power is on the controllable 3V3_S rail
#define PIN_GPS_PPS (17) // Pulse per second input from the GPS

#define PIN_GPS_RX PIN_SERIAL1_RX
#define PIN_GPS_TX PIN_SERIAL1_TX
#define PIN_GPS_RX PIN_SERIAL1_TX
#define PIN_GPS_TX PIN_SERIAL1_RX

#define PIN_GPS_1PPS PIN_GPS_PPS
#define GPS_BAUD_RATE 9600
Expand Down
9 changes: 4 additions & 5 deletions variants/rak4631/platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@ build_flags = ${nrf52_base.build_flags}
-D NRF52_POWER_MANAGEMENT
-D PIN_BOARD_SCL=14
-D PIN_BOARD_SDA=13
-D PIN_GPS_TX=PIN_SERIAL1_RX
-D PIN_GPS_RX=PIN_SERIAL1_TX
-D FORCE_GPS_ALIVE
-D PIN_GPS_EN=-1
-D PIN_OLED_RESET=-1
-D USE_SX1262
Expand Down Expand Up @@ -122,7 +121,7 @@ board_upload.maximum_size = 712704
build_flags =
${rak4631.build_flags}
-I examples/companion_radio/ui-new
-D PIN_USER_BTN=9
-D PIN_USER_BTN=9 ; P0.09 = IO5: incompatible with Slot D peripherals (e.g. RAK12002 CLKOUT holds it low)
-D PIN_USER_BTN_ANA=31
-D DISPLAY_CLASS=SSD1306Display
-D MAX_CONTACTS=350
Expand All @@ -143,7 +142,7 @@ board_upload.maximum_size = 712704
build_flags =
${rak4631.build_flags}
-I examples/companion_radio/ui-new
-D PIN_USER_BTN=9
-D PIN_USER_BTN=9 ; P0.09 = IO5: incompatible with Slot D peripherals (e.g. RAK12002 CLKOUT holds it low)
-D PIN_USER_BTN_ANA=31
-D DISPLAY_CLASS=SSD1306Display
-D MAX_CONTACTS=350
Expand All @@ -165,7 +164,7 @@ lib_deps =
extends = rak4631
build_flags =
${rak4631.build_flags}
-D PIN_USER_BTN=9
-D PIN_USER_BTN=9 ; P0.09 = IO5: incompatible with Slot D peripherals (e.g. RAK12002 CLKOUT holds it low)
-D PIN_USER_BTN_ANA=31
-D MAX_CONTACTS=100
-D MAX_GROUP_CHANNELS=1
Expand Down
3 changes: 3 additions & 0 deletions variants/rak4631/variant.h
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,9 @@ extern "C"
#define EXTERNAL_FLASH_DEVICES IS25LP080D
#define EXTERNAL_FLASH_USE_QSPI

#define PIN_GPS_RX PIN_SERIAL1_TX
#define PIN_GPS_TX PIN_SERIAL1_RX

#define PIN_GPS_1PPS 17 //GPS PPS pin
#define GPS_BAUD_RATE 9600
#define GPS_ADDRESS 0x42 //i2c address for GPS
Expand Down