Improved GPS Settings app and GPS service (#222)

- Fixes and improvements to `GpsSettings` app, `GpsDevice` and `GpsService`
- Implemented location/GPS statusbar icon
- Added app icon
- Added support for other GPS models (based on Meshtastic code)
This commit is contained in:
Ken Van Hoeylandt
2025-02-18 22:07:37 +01:00
committed by GitHub
parent ad2cad3bf1
commit 5055fa7822
30 changed files with 2130 additions and 384 deletions
@@ -2,7 +2,6 @@
#include "../Device.h"
#include "../uart/Uart.h"
#include "GpsDeviceInitDefault.h"
#include "Satellites.h"
#include <Tactility/Mutex.h>
@@ -13,51 +12,88 @@
namespace tt::hal::gps {
enum class GpsResponse {
None,
NotAck,
FrameErrors,
Ok,
};
enum class GpsModel {
Unknown = 0,
AG3335,
AG3352,
ATGM336H,
LS20031,
MTK,
MTK_L76B,
MTK_PA1616S,
UBLOX6,
UBLOX7,
UBLOX8,
UBLOX9,
UBLOX10,
UC6580,
};
const char* toString(GpsModel model);
class GpsDevice : public Device {
public:
typedef int SatelliteSubscriptionId;
typedef int LocationSubscriptionId;
typedef int GgaSubscriptionId;
typedef int RmcSubscriptionId;
struct Configuration {
std::string name;
uart_port_t uartPort;
uint32_t baudRate;
std::function<bool(uart_port_t)> initFunction = initGpsDefault;
GpsModel model;
};
enum class State {
PendingOn,
On,
Error,
PendingOff,
Off
};
private:
struct SatelliteSubscription {
SatelliteSubscriptionId id;
std::shared_ptr<std::function<void(Device::Id id, const minmea_sat_info&)>> onData;
struct GgaSubscription {
GgaSubscriptionId id;
std::shared_ptr<std::function<void(Device::Id id, const minmea_sentence_gga&)>> onData;
};
struct LocationSubscription {
LocationSubscriptionId id;
struct RmcSubscription {
RmcSubscriptionId id;
std::shared_ptr<std::function<void(Device::Id id, const minmea_sentence_rmc&)>> onData;
};
const Configuration configuration;
Mutex mutex;
Mutex mutex = Mutex(Mutex::Type::Recursive);
std::unique_ptr<Thread> thread;
bool threadInterrupted = false;
std::vector<SatelliteSubscription> satelliteSubscriptions;
std::vector<LocationSubscription> locationSubscriptions;
SatelliteSubscriptionId lastSatelliteSubscriptionId = 0;
LocationSubscriptionId lastLocationSubscriptionId = 0;
std::vector<GgaSubscription> ggaSubscriptions;
std::vector<RmcSubscription> rmcSubscriptions;
GgaSubscriptionId lastSatelliteSubscriptionId = 0;
RmcSubscriptionId lastRmcSubscriptionId = 0;
GpsModel model = GpsModel::Unknown;
State state = State::Off;
static int32_t threadMainStatic(void* parameter);
int32_t threadMain();
bool isThreadInterrupted() const;
void setState(State newState);
public:
explicit GpsDevice(Configuration configuration) : configuration(std::move(configuration)) {
assert(this->configuration.initFunction != nullptr);
}
explicit GpsDevice(Configuration configuration) : configuration(std::move(configuration)) {}
~GpsDevice() override = default;
@@ -69,31 +105,41 @@ public:
bool start();
bool stop();
bool isStarted() const;
SatelliteSubscriptionId subscribeSatellites(const std::function<void(Device::Id deviceId, const minmea_sat_info&)>& onData) {
satelliteSubscriptions.push_back({
GgaSubscriptionId subscribeGga(const std::function<void(Device::Id deviceId, const minmea_sentence_gga&)>& onData) {
auto lock = mutex.asScopedLock();
lock.lock();
ggaSubscriptions.push_back({
.id = ++lastSatelliteSubscriptionId,
.onData = std::make_shared<std::function<void(Device::Id, const minmea_sat_info&)>>(onData)
.onData = std::make_shared<std::function<void(Device::Id, const minmea_sentence_gga&)>>(onData)
});
return lastSatelliteSubscriptionId;
}
void unsubscribeSatellites(SatelliteSubscriptionId subscriptionId) {
std::erase_if(satelliteSubscriptions, [subscriptionId](auto& subscription) { return subscription.id == subscriptionId; });
void unsubscribeGga(GgaSubscriptionId subscriptionId) {
auto lock = mutex.asScopedLock();
lock.lock();
std::erase_if(ggaSubscriptions, [subscriptionId](auto& subscription) { return subscription.id == subscriptionId; });
}
LocationSubscriptionId subscribeLocations(const std::function<void(Device::Id deviceId, const minmea_sentence_rmc&)>& onData) {
locationSubscriptions.push_back({
.id = ++lastLocationSubscriptionId,
RmcSubscriptionId subscribeRmc(const std::function<void(Device::Id deviceId, const minmea_sentence_rmc&)>& onData) {
auto lock = mutex.asScopedLock();
lock.lock();
rmcSubscriptions.push_back({
.id = ++lastRmcSubscriptionId,
.onData = std::make_shared<std::function<void(Device::Id, const minmea_sentence_rmc&)>>(onData)
});
return lastLocationSubscriptionId;
return lastRmcSubscriptionId;
}
void unsubscribeLocations(SatelliteSubscriptionId subscriptionId) {
std::erase_if(locationSubscriptions, [subscriptionId](auto& subscription) { return subscription.id == subscriptionId; });
void unsubscribeRmc(GgaSubscriptionId subscriptionId) {
auto lock = mutex.asScopedLock();
lock.lock();
std::erase_if(rmcSubscriptions, [subscriptionId](auto& subscription) { return subscription.id == subscriptionId; });
}
GpsModel getModel() const;
State getState() const;
};
}
@@ -1,9 +0,0 @@
#pragma once
#include <Tactility/hal/uart/Uart.h>
namespace tt::hal::gps {
bool initGpsDefault(uart_port_t port);
}
@@ -111,7 +111,10 @@ void flushInput(uart_port_t port);
*/
std::string readStringUntil(uart_port_t port, char untilChar, TickType_t timeout = defaultTimeout);
/** Read a buffer as a byte array until the specified character (the "untilChar" is included in the result) */
bool readUntil(uart_port_t port, uint8_t* buffer, size_t bufferSize, uint8_t untilByte, TickType_t timeout = defaultTimeout);
/**
* Read a buffer as a byte array until the specified character (the "untilChar" is included in the result)
* @return the amount of bytes read from UART
*/
size_t readUntil(uart_port_t port, uint8_t* buffer, size_t bufferSize, uint8_t untilByte, TickType_t timeout = defaultTimeout, bool addNullTerminator = true);
} // namespace tt::hal::uart
@@ -1,6 +1,9 @@
#pragma once
#include "Tactility/hal/gps/GpsDevice.h"
#include "GpsState.h"
#include <Tactility/PubSub.h>
namespace tt::service::gps {
@@ -16,7 +19,13 @@ bool startReceiving();
/** Turn GPS receiving off */
void stopReceiving();
/** @return true when GPS receiver is on and 1 or more devices are active */
bool isReceiving();
bool hasCoordinates();
bool getCoordinates(minmea_sentence_rmc& rmc);
State getState();
/** @return GPS service pubsub that broadcasts State* objects */
std::shared_ptr<PubSub> getStatePubsub();
}
@@ -0,0 +1,12 @@
#pragma once
namespace tt::service::gps {
enum class State {
OnPending,
On,
OffPending,
Off
};
}
@@ -76,7 +76,7 @@ struct ApRecord {
};
/**
* @brief Get wifi pubsub
* @brief Get wifi pubsub that broadcasts Event objects
* @return PubSub
*/
std::shared_ptr<PubSub> getPubsub();