diff --git a/src/helpers/nrf52/RAK4631Board.h b/src/helpers/nrf52/RAK4631Board.h index a389135d..9232e39c 100644 --- a/src/helpers/nrf52/RAK4631Board.h +++ b/src/helpers/nrf52/RAK4631Board.h @@ -12,6 +12,17 @@ #define P_LORA_MISO 45 #define P_LORA_MOSI 44 #define SX126X_POWER_EN 37 + +#define P_GPS_SDA 13 //GPS SDA pin (output option) +#define P_GPS_SCL 14 //GPS SCL pin (output option) +#define P_GPS_TX 16 //GPS TX pin +#define P_GPS_RX 15 //GPS RX pin +#define P_GPS_STANDBY_A 34 //GPS Reset/Standby pin (IO2 for socket A) +#define P_GPS_STANDBY_C 4 //GPS Reset/Standby pin (IO4 for socket C) +#define P_GPS_STANDBY_F 9 //GPS Reset/Standby pin (IO5 for socket F) +#define P_GPS_1PPS 17 //GPS PPS pin +#define GPS_BAUD_RATE 9600 +#define GPS_ADDRESS 0x42 //i2c address for GPS #define SX126X_DIO2_AS_RF_SWITCH true #define SX126X_DIO3_TCXO_VOLTAGE 1.8 diff --git a/variants/lilygo_tbeam_supreme_SX1262/target.cpp b/variants/lilygo_tbeam_supreme_SX1262/target.cpp index 4b38b11d..47a68948 100644 --- a/variants/lilygo_tbeam_supreme_SX1262/target.cpp +++ b/variants/lilygo_tbeam_supreme_SX1262/target.cpp @@ -336,7 +336,7 @@ bool TbeamSupSensorManager::begin() { } bool TbeamSupSensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { - if (requester_permissions & TELEM_PERM_LOCATION) { // does requester have permission? + if (requester_permissions & TELEM_PERM_LOCATION && gps_active) { // does requester have permission? telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); } if (requester_permissions & TELEM_PERM_ENVIRONMENT && bme_active) { // does requester have permission? diff --git a/variants/rak4631/platformio.ini b/variants/rak4631/platformio.ini index c2845fd4..54fd063a 100644 --- a/variants/rak4631/platformio.ini +++ b/variants/rak4631/platformio.ini @@ -22,6 +22,8 @@ build_src_filter = ${nrf52840_base.build_src_filter} lib_deps = ${nrf52840_base.lib_deps} adafruit/Adafruit SSD1306 @ ^2.5.13 + stevemarple/MicroNMEA @ ^2.0.6 + sparkfun/SparkFun u-blox GNSS Arduino Library @ ^2.2.27 [env:RAK_4631_Repeater] extends = rak4631 diff --git a/variants/rak4631/target.cpp b/variants/rak4631/target.cpp index 9b23af83..1f55dac1 100644 --- a/variants/rak4631/target.cpp +++ b/variants/rak4631/target.cpp @@ -1,6 +1,7 @@ #include #include "target.h" #include +#include RAK4631Board board; @@ -10,7 +11,8 @@ WRAPPER_CLASS radio_driver(radio, board); VolatileRTCClock fallback_clock; AutoDiscoverRTCClock rtc_clock(fallback_clock); -SensorManager sensors; +MicroNMEALocationProvider nmea = MicroNMEALocationProvider(Wire); +RAK4631SensorManager sensors = RAK4631SensorManager(nmea); #ifdef DISPLAY_CLASS DISPLAY_CLASS display; @@ -20,6 +22,68 @@ SensorManager sensors; #define LORA_CR 5 #endif +#ifdef MESH_DEBUG +uint32_t deviceOnline = 0x00; +void scanDevices(TwoWire *w) +{ + uint8_t err, addr; + int nDevices = 0; + uint32_t start = 0; + + Serial.println("Scanning I2C for Devices"); + for (addr = 1; addr < 127; addr++) { + start = millis(); + w->beginTransmission(addr); delay(2); + err = w->endTransmission(); + if (err == 0) { + nDevices++; + switch (addr) { + case 0x42: + Serial.println("\tFound RAK12500 GPS Sensor"); + deviceOnline |= RAK12500_ONLINE; + break; + default: + Serial.print("\tI2C device found at address 0x"); + if (addr < 16) { + Serial.print("0"); + } + Serial.print(addr, HEX); + Serial.println(" !"); + break; + } + + } else if (err == 4) { + Serial.print("Unknow error at address 0x"); + if (addr < 16) { + Serial.print("0"); + } + Serial.println(addr, HEX); + } + } + if (nDevices == 0) + Serial.println("No I2C devices found\n"); + + Serial.println("Scan for devices is complete."); + Serial.println("\n"); +} +#endif + +static bool readStringUntil(Stream& s, char dest[], size_t max_len, char term, unsigned int timeout_millis) { + unsigned long timeout = millis() + timeout_millis; + char *dp = dest; + while (millis() < timeout && dp - dest < max_len - 1) { + if (s.available()) { + char c = s.read(); + if (c == term) break; + *dp++ = c; // append to dest[] + } else { + delay(1); + } + } + *dp = 0; // null terminator + return millis() < timeout; // false, if timed out +} + bool radio_init() { rtc_clock.begin(Wire); @@ -68,6 +132,136 @@ void radio_set_tx_power(uint8_t dbm) { radio.setOutputPower(dbm); } +void RAK4631SensorManager::start_gps() +{ + //function currently not used + gps_active = true; + pinMode(disStandbyPin, OUTPUT); + digitalWrite(disStandbyPin, 1); + MESH_DEBUG_PRINTLN("GPS should be on now"); +} + +void RAK4631SensorManager::stop_gps() +{ + //function currently not used + gps_active = false; + pinMode(disStandbyPin, OUTPUT); + digitalWrite(disStandbyPin, 0); + MESH_DEBUG_PRINTLN("GPS should be off now"); +} + +void RAK4631SensorManager::sleep_gps() { + gps_active = false; + ublox_GNSS.powerSaveMode(); + MESH_DEBUG_PRINTLN("GPS should be sleeping now"); +} + +void RAK4631SensorManager::wake_gps() { + gps_active = true; + ublox_GNSS.powerSaveMode(false); + MESH_DEBUG_PRINTLN("GPS should be waking now"); +} + +bool RAK4631SensorManager::gpsIsAwake(uint32_t ioPin){ + + int pinInitialState = 0; + + //set initial waking state + pinMode(ioPin,OUTPUT); + digitalWrite(ioPin,0); + delay(1000); + digitalWrite(ioPin,1); + delay(1000); + + if (ublox_GNSS.begin(Wire) == true){ + MESH_DEBUG_PRINTLN("GPS init correctly and GPS is turned on"); + ublox_GNSS.setI2COutput(COM_TYPE_NMEA); + ublox_GNSS.saveConfigSelective(VAL_CFG_SUBSEC_IOPORT); + disStandbyPin = ioPin; + gps_active = true; + gps_present = true; + return true; + } + else + MESH_DEBUG_PRINTLN("GPS failed to init on this IO pin... try the next"); + //digitalWrite(ioPin,pinInitialState); //reset the IO pin to initial state + return false; +} + +bool RAK4631SensorManager::begin() { + + #ifdef MESH_DEBUG + scanDevices(&Wire); + #endif + + //search for the correct IO standby pin depending on socket used + if(gpsIsAwake(P_GPS_STANDBY_A)){ + MESH_DEBUG_PRINTLN("GPS is on socket A"); + } + else if(gpsIsAwake(P_GPS_STANDBY_C)){ + MESH_DEBUG_PRINTLN("GPS is on socket C"); + } + else if(gpsIsAwake(P_GPS_STANDBY_F)){ + MESH_DEBUG_PRINTLN("GPS is on socket F"); + } + else{ + MESH_DEBUG_PRINTLN("Error: No GPS found on sockets A, C or F"); + gps_active = false; + gps_present = false; + return false; + } + + //Now that GPS is found and set up, set to sleep for initial state + stop_gps(); +} + +bool RAK4631SensorManager::querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) { + if (requester_permissions & TELEM_PERM_LOCATION && gps_active) { // does requester have permission? + telemetry.addGPS(TELEM_CHANNEL_SELF, node_lat, node_lon, node_altitude); + } + return true; +} + +void RAK4631SensorManager::loop() { + static long next_update = 0; + + _nmea->loop(); + + if (millis() > next_update && gps_active) { + node_lat = (double)ublox_GNSS.getLatitude()/10000000.; + node_lon = (double)ublox_GNSS.getLongitude()/10000000.; + node_altitude = (double)ublox_GNSS.getAltitude()/1000.; + MESH_DEBUG_PRINT("lat %f lon %f alt %f\r\n", node_lat, node_lon, node_altitude); + + next_update = millis() + 1000; + } +} + +int RAK4631SensorManager::getNumSettings() const { return 1; } // just one supported: "gps" (power switch) + +const char* RAK4631SensorManager::getSettingName(int i) const { + return i == 0 ? "gps" : NULL; +} + +const char* RAK4631SensorManager::getSettingValue(int i) const { + if (i == 0) { + return gps_active ? "1" : "0"; + } + return NULL; +} + +bool RAK4631SensorManager::setSettingValue(const char* name, const char* value) { + if (strcmp(name, "gps") == 0) { + if (strcmp(value, "0") == 0) { + stop_gps(); + } else { + start_gps(); + } + return true; + } + return false; // not supported +} + mesh::LocalIdentity radio_new_identity() { RadioNoiseListener rng(radio); return mesh::LocalIdentity(&rng); // create new random identity diff --git a/variants/rak4631/target.h b/variants/rak4631/target.h index a50d6f2c..7ae3e914 100644 --- a/variants/rak4631/target.h +++ b/variants/rak4631/target.h @@ -7,19 +7,65 @@ #include #include #include +#include +#include #ifdef DISPLAY_CLASS #include #endif +#define _BV(x) (1 << x) + +class RAK4631SensorManager: public SensorManager { + bool gps_active = false; + bool gps_present = false; + LocationProvider * _nmea; + SFE_UBLOX_GNSS ublox_GNSS; + uint32_t disStandbyPin = 0; + + void start_gps(); + void stop_gps(); + void sleep_gps(); + void wake_gps(); + bool gpsIsAwake(uint32_t ioPin); + + public: + RAK4631SensorManager(LocationProvider &nmea): _nmea(&nmea) { } + bool begin() override; + bool querySensors(uint8_t requester_permissions, CayenneLPP& telemetry) override; + void loop() override; + int getNumSettings() const override; + const char* getSettingName(int i) const override; + const char* getSettingValue(int i) const override; + bool setSettingValue(const char* name, const char* value) override; + }; + extern RAK4631Board board; extern WRAPPER_CLASS radio_driver; extern AutoDiscoverRTCClock rtc_clock; -extern SensorManager sensors; +extern RAK4631SensorManager sensors; #ifdef DISPLAY_CLASS extern DISPLAY_CLASS display; #endif +enum { + POWERMANAGE_ONLINE = _BV(0), + DISPLAY_ONLINE = _BV(1), + RADIO_ONLINE = _BV(2), + GPS_ONLINE = _BV(3), + PSRAM_ONLINE = _BV(4), + SDCARD_ONLINE = _BV(5), + AXDL345_ONLINE = _BV(6), + BME280_ONLINE = _BV(7), + BMP280_ONLINE = _BV(8), + BME680_ONLINE = _BV(9), + QMC6310_ONLINE = _BV(10), + QMI8658_ONLINE = _BV(11), + PCF8563_ONLINE = _BV(12), + OSC32768_ONLINE = _BV(13), + RAK12500_ONLINE = _BV(14), +}; + bool radio_init(); uint32_t radio_get_rng_seed(); void radio_set_params(float freq, float bw, uint8_t sf, uint8_t cr);