From ad0a1b1e3dc19efd2e14f9fe4e08e2231ecf2735 Mon Sep 17 00:00:00 2001 From: sam rolfe Date: Fri, 5 Sep 2025 10:41:10 +1000 Subject: [PATCH] Core loop: telemetry A sec, command poll B sec; car-power OFF -> no-power POST + sleep C; WiFi home connect placeholder; GNSS placeholders --- CAR_GPS_TRACKER.ino | 117 +++++++++++++++++++++++++++++------------ src/app/AppConfig.hpp | 21 ++++++++ src/core/Telemetry.hpp | 7 ++- src/hw/PowerMgr.cpp | 16 ++++++ src/hw/PowerMgr.hpp | 11 ++++ src/hw/WiFiMgr.cpp | 19 +++++++ src/hw/WiFiMgr.hpp | 8 +++ 7 files changed, 163 insertions(+), 36 deletions(-) diff --git a/CAR_GPS_TRACKER.ino b/CAR_GPS_TRACKER.ino index 49cb6a1..3af325d 100644 --- a/CAR_GPS_TRACKER.ino +++ b/CAR_GPS_TRACKER.ino @@ -2,51 +2,100 @@ #include "src/core/HttpClient.hpp" #include "src/core/SmsMgr.hpp" #include "src/core/CommandMgr.hpp" +#include "src/core/Telemetry.hpp" +#include "src/app/AppConfig.hpp" +#include "src/hw/PowerMgr.hpp" +#include "src/hw/WiFiMgr.hpp" -static String DEVICE_IMEI = ""; - -static const char* APN = "hologram"; -static const char* BASE_URL = "http://laravel-server.lab.audasmedia.com.au"; -static const char* PATH = "/api/gps"; +static AppConfig CFG; static String queryImei() { -String r = NetMgr::sendAT("AT+CGSN", 3000); -// Response often includes the IMEI line + OK; take the first 15-digit line -int s = 0; -for (int i=0;i<(int)r.length();++i) { -if (isDigit(r[i])) { s = i; break; } -} -String imei = r.substring(s, s+15); -imei.trim(); -return imei; + String r = NetMgr::sendAT("AT+CGSN", 3000); + int s = -1; + for (int i = 0; i < (int)r.length() - 14; ++i) { if (isDigit(r[i])) { s = i; break; } } + if (s >= 0) { String imei = r.substring(s, s + 15); imei.trim(); return imei; } + return ""; } void setup() { -Serial.begin(115200); -Serial2.begin(9600, SERIAL_8N1, 16, 17); // RX=16, TX=17 + Serial.begin(115200); + Serial2.begin(9600, SERIAL_8N1, 16, 17); -NetMgr::powerOnSIM7080(4); // PWRKEY pin 4 -NetMgr::attachAndPdp(APN); // LTE reg + CNACT + DNS -SmsMgr::setup(); // enable text mode + URCs + // 1) Radio attach + NetMgr::powerOnSIM7080(4); + NetMgr::attachAndPdp(CFG.apn.c_str()); + SmsMgr::setup(); -DEVICE_IMEI = queryImei(); -if (DEVICE_IMEI.length()==0) DEVICE_IMEI = "860016049744324"; // fallback if needed + // 2) Identity + CFG.imei = queryImei(); + if (CFG.imei.isEmpty()) CFG.imei = "860016049744324"; -CommandMgr::configure(BASE_URL, DEVICE_IMEI); + // 3) Init power sim + PowerMgr::init(true); // car power ON initially (simulate) -double lat,lng,alt,spd,hdg; -Telemetry::readGNSS(lat,lng,alt,spd,hdg); -String json = Telemetry::buildJson(DEVICE_IMEI, lat,lng,alt,spd,hdg); + // 4) Command manager config + CommandMgr::configure(CFG.baseUrl, CFG.imei); -//String json = -// "{\"device_id\":\"sim7080g-01\",\"lat\":-33.865143," -// "\"lng\":151.2099,\"speed\":12.5,\"altitude\":30.2}"; -HttpClient::postJson(BASE_URL, PATH, json); + // GNSS power on (if applicable later) + NetMgr::sendAT("AT+CGNSPWR=1", 2000); } void loop() { -SmsMgr::pollUrc(); // non‑blocking URC scan; handles +CMTI -CommandMgr::poll(30000); // every 30s -SmsMgr::pollUnread(); // optional safety poll -delay(250); -} + static uint32_t lastTelemetryMs = 0; + static uint32_t lastPollMs = 0; + static bool noPowerSent = false; + + SmsMgr::pollUrc(); // SMS commands + + bool carOn = PowerMgr::isCarPowerOn(); + + // When car power is ON + if (carOn) { + noPowerSent = false; // reset for next OFF cycle + + // Telemetry every A seconds + if (millis() - lastTelemetryMs >= CFG.telemetryPeriodSec * 1000UL) { + double lat, lng, alt, spd, hdg; + Telemetry::read(lat, lng, alt, spd, hdg); + String json = Telemetry::buildJson(CFG.imei, lat, lng, alt, spd, hdg, carOn); + HttpClient::postJson(CFG.baseUrl, CFG.telemetryPath, json); + lastTelemetryMs = millis(); + } + + // Poll commands every B seconds + if (millis() - lastPollMs >= CFG.commandPollSec * 1000UL) { + CommandMgr::poll(CFG.commandPollSec * 1000UL); // uses its own timer, but call to force + lastPollMs = millis(); + } + } + // When car power is OFF + else { + if (!noPowerSent) { + String np = Telemetry::buildNoPowerJson(CFG.imei); + HttpClient::postJson(CFG.baseUrl, CFG.telemetryPath, np); + noPowerSent = true; + } + + // One last command poll to check instructions (e.g., connect Wi‑Fi, sleep C seconds) + CommandMgr::poll(0); + + // Placeholder: if server requested home Wi‑Fi, connect + if (CFG.atHomeRequested) { + bool ok = WiFiMgr::connectHome("Aussie Broadband 8729", "Ffdfmunfca", 15000); + Serial.println(ok ? "Home WiFi connected" : "Home WiFi failed"); + if (ok) { + // You can add local/LAN interactions here; then disconnect: + WiFiMgr::disconnect(); + } + CFG.atHomeRequested = false; + } + + // Go to deep sleep (placeholder – currently just delay) + PowerMgr::deepSleep(CFG.sleepSec); + delay(CFG.sleepSec * 1000UL); + // On real deep sleep, code resumes at setup() on wake + // If simulating, continue loop + } + + delay(100); +} \ No newline at end of file diff --git a/src/app/AppConfig.hpp b/src/app/AppConfig.hpp index e69de29..e9668e9 100644 --- a/src/app/AppConfig.hpp +++ b/src/app/AppConfig.hpp @@ -0,0 +1,21 @@ +#pragma once +#include + +struct AppConfig { + // periods (seconds) + uint32_t telemetryPeriodSec = 30; // A + uint32_t commandPollSec = 30; // B + uint32_t sleepSec = 300; // C + + // device state + bool carPowerOn = true; // simulate for now + bool atHomeRequested = false; // server may request WiFi connect + + // networking + String apn = "hologram"; + String baseUrl = "http://laravel-server.lab.audasmedia.com.au"; + String telemetryPath = "/api/gps"; + + // identity + String imei; // set at runtime +}; \ No newline at end of file diff --git a/src/core/Telemetry.hpp b/src/core/Telemetry.hpp index dfb1529..f3d7300 100644 --- a/src/core/Telemetry.hpp +++ b/src/core/Telemetry.hpp @@ -1,6 +1,9 @@ +// Telemetry.hpp #pragma once #include + namespace Telemetry { - bool readGNSS(double& lat, double& lng, double& alt, double& speed, double& heading); - String buildJson(const String& imei, double lat, double lng, double alt, double speed, double heading); + bool read(double& lat, double& lng, double& alt, double& speed, double& heading); + String buildJson(const String& imei, double lat, double lng, double alt, double speed, double heading, bool carPower); + String buildNoPowerJson(const String& imei); } \ No newline at end of file diff --git a/src/hw/PowerMgr.cpp b/src/hw/PowerMgr.cpp index e69de29..277d820 100644 --- a/src/hw/PowerMgr.cpp +++ b/src/hw/PowerMgr.cpp @@ -0,0 +1,16 @@ +// PowerMgr.cpp +#include "PowerMgr.hpp" + +namespace { + bool carOn = true; +} + +namespace PowerMgr { + void init(bool carPowerInitial){ carOn = carPowerInitial; } + bool isCarPowerOn(){ return carOn; } + void setCarPower(bool on){ carOn = on; } + void deepSleep(uint32_t /*sec*/) { + // placeholder: implement esp_deep_sleep() later + // For now, just delay to simulate + } +} \ No newline at end of file diff --git a/src/hw/PowerMgr.hpp b/src/hw/PowerMgr.hpp index e69de29..84aa234 100644 --- a/src/hw/PowerMgr.hpp +++ b/src/hw/PowerMgr.hpp @@ -0,0 +1,11 @@ +// PowerMgr.hpp +#pragma once +#include + +namespace PowerMgr { + void init(bool carPowerInitial); + bool isCarPowerOn(); + void setCarPower(bool on); // simulate change + // called before deep sleep; placeholder + void deepSleep(uint32_t sec); +} \ No newline at end of file diff --git a/src/hw/WiFiMgr.cpp b/src/hw/WiFiMgr.cpp index e69de29..c86115f 100644 --- a/src/hw/WiFiMgr.cpp +++ b/src/hw/WiFiMgr.cpp @@ -0,0 +1,19 @@ +// WiFiMgr.cpp +#include "WiFiMgr.hpp" +#include // ESP32 WiFi + +namespace WiFiMgr { + bool connectHome(const char* ssid, const char* psk, uint32_t timeoutMs) { + WiFi.mode(WIFI_STA); + WiFi.begin(ssid, psk); + uint32_t t0 = millis(); + while (WiFi.status() != WL_CONNECTED && millis() - t0 < timeoutMs) { + delay(200); + } + return WiFi.status() == WL_CONNECTED; + } + void disconnect() { + WiFi.disconnect(true, true); + WiFi.mode(WIFI_OFF); + } +} \ No newline at end of file diff --git a/src/hw/WiFiMgr.hpp b/src/hw/WiFiMgr.hpp index e69de29..67b530c 100644 --- a/src/hw/WiFiMgr.hpp +++ b/src/hw/WiFiMgr.hpp @@ -0,0 +1,8 @@ +// WiFiMgr.hpp +#pragma once +#include + +namespace WiFiMgr { + bool connectHome(const char* ssid, const char* psk, uint32_t timeoutMs = 15000); + void disconnect(); +} \ No newline at end of file