diff --git a/CAR_GPS_TRACKER.ino b/CAR_GPS_TRACKER.ino index 0299f5f..49cb6a1 100644 --- a/CAR_GPS_TRACKER.ino +++ b/CAR_GPS_TRACKER.ino @@ -10,38 +10,43 @@ static const char* BASE_URL = "http://laravel-server.lab.audasmedia.com.au"; static const char* PATH = "/api/gps"; 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); +// 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; } 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); // RX=16, TX=17 - NetMgr::powerOnSIM7080(4); // PWRKEY pin 4 - NetMgr::attachAndPdp(APN); // LTE reg + CNACT + DNS - SmsMgr::setup(); // enable text mode + URCs +NetMgr::powerOnSIM7080(4); // PWRKEY pin 4 +NetMgr::attachAndPdp(APN); // LTE reg + CNACT + DNS +SmsMgr::setup(); // enable text mode + URCs - DEVICE_IMEI = queryImei(); - if (DEVICE_IMEI.length()==0) DEVICE_IMEI = "860016049744324"; // fallback if needed +DEVICE_IMEI = queryImei(); +if (DEVICE_IMEI.length()==0) DEVICE_IMEI = "860016049744324"; // fallback if needed - CommandMgr::configure(BASE_URL, DEVICE_IMEI); +CommandMgr::configure(BASE_URL, DEVICE_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); +double lat,lng,alt,spd,hdg; +Telemetry::readGNSS(lat,lng,alt,spd,hdg); +String json = Telemetry::buildJson(DEVICE_IMEI, lat,lng,alt,spd,hdg); + +//String json = +// "{\"device_id\":\"sim7080g-01\",\"lat\":-33.865143," +// "\"lng\":151.2099,\"speed\":12.5,\"altitude\":30.2}"; +HttpClient::postJson(BASE_URL, PATH, json); } void loop() { - SmsMgr::pollUrc(); // non‑blocking URC scan; handles +CMTI - CommandMgr::poll(30000); // every 30s - SmsMgr::pollUnread(); // optional safety poll - delay(250); -} \ No newline at end of file +SmsMgr::pollUrc(); // non‑blocking URC scan; handles +CMTI +CommandMgr::poll(30000); // every 30s +SmsMgr::pollUnread(); // optional safety poll +delay(250); +} diff --git a/backup/CAR_GPS_TRACKER (Copy).ino b/backup/CAR_GPS_TRACKER (Copy).ino new file mode 100644 index 0000000..0299f5f --- /dev/null +++ b/backup/CAR_GPS_TRACKER (Copy).ino @@ -0,0 +1,47 @@ +#include "src/core/NetMgr.hpp" +#include "src/core/HttpClient.hpp" +#include "src/core/SmsMgr.hpp" +#include "src/core/CommandMgr.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 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; +} + +void setup() { + Serial.begin(115200); + Serial2.begin(9600, SERIAL_8N1, 16, 17); // RX=16, TX=17 + + NetMgr::powerOnSIM7080(4); // PWRKEY pin 4 + NetMgr::attachAndPdp(APN); // LTE reg + CNACT + DNS + SmsMgr::setup(); // enable text mode + URCs + + DEVICE_IMEI = queryImei(); + if (DEVICE_IMEI.length()==0) DEVICE_IMEI = "860016049744324"; // fallback if needed + + CommandMgr::configure(BASE_URL, DEVICE_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); +} + +void loop() { + SmsMgr::pollUrc(); // non‑blocking URC scan; handles +CMTI + CommandMgr::poll(30000); // every 30s + SmsMgr::pollUnread(); // optional safety poll + delay(250); +} \ No newline at end of file diff --git a/backup/CAR_GPS_TRACKER_working_GET.ino b/backup/CAR_GPS_TRACKER_working_GET.ino new file mode 100644 index 0000000..29ff0ba --- /dev/null +++ b/backup/CAR_GPS_TRACKER_working_GET.ino @@ -0,0 +1,209 @@ +#include + +#define RXD2 16 +#define TXD2 17 +#define PWRKEY 4 + + +int parseLenFromSHREQ(const String& buf) { + int start = 0; + while (start < (int)buf.length()) { + int end = buf.indexOf('\n', start); + if (end == -1) end = buf.length(); + String line = buf.substring(start, end); + line.trim(); + if (line.startsWith("+SHREQ:")) { + int lastComma = line.lastIndexOf(','); + if (lastComma < 0) return -1; + int i = lastComma + 1; + while (i < (int)line.length() && line[i] == ' ') i++; + int j = i; + while (j < (int)line.length() && isDigit(line[j])) j++; + if (j <= i) return -1; + return line.substring(i, j).toInt(); + } + start = end + 1; + } + return -1; +} + + +String at(const String& cmd, uint32_t timeoutMs=5000, uint32_t urcWindowMs=500) { + Serial.print(">> "); Serial.println(cmd); + while (Serial2.available()) (void)Serial2.read(); + Serial2.println(cmd); + + String resp; + uint32_t t0 = millis(); + while (millis()-t0 < timeoutMs) { + while (Serial2.available()) { + char c = (char)Serial2.read(); + resp += c; Serial.print(c); + t0 = millis(); // extend on activity + } + } + + // Short URC window to catch lines like +SHREQ: "GET",200, + uint32_t u0 = millis(); + while (millis()-u0 < urcWindowMs) { + while (Serial2.available()) { + char c = (char)Serial2.read(); + resp += c; Serial.print(c); + u0 = millis(); // extend on activity + } + } + + resp.trim(); + Serial.println("\n[Resp]\n" + resp); + return resp; +} + +void powerOnSIM7080() { + pinMode(PWRKEY, OUTPUT); + digitalWrite(PWRKEY, LOW); delay(2000); + digitalWrite(PWRKEY, HIGH); delay(1000); + digitalWrite(PWRKEY, LOW); delay(30000); +} + +bool waitLTE(uint32_t ms=120000) { + uint32_t t0=millis(); + while (millis()-t0 < ms) { + String r = at("AT+CEREG?", 3000); + if (r.indexOf("+CEREG: 0,1")!=-1 || r.indexOf("+CEREG: 0,5")!=-1) return true; + delay(2000); + } + return false; +} + +void attachPDP() { + at("AT",2000); + at("AT+CMEE=1",2000); + at("AT+CPIN?",2000); + at("AT+CSQ",2000); + at("AT+CNMP=38",3000); + at("AT+CMNB=1",3000); + at("AT+CGDCONT=1,\"IP\",\"hologram\"",3000); + at("AT+CGATT=1",10000); + if (!waitLTE()) { Serial.println("No LTE"); while(true) delay(1000); } + at("AT+CNCFG=0,1,\"hologram\"",3000); + at("AT+CNACT=0,1",20000); + at("AT+CNACT?",2000); + at("AT+CDNSCFG=\"8.8.8.8\",\"1.1.1.1\"",2000); +} + +// Pure SH GET for one host+path, prints raw SHREAD output(s) +void shGet(const String& base, const String& path) { + Serial.println("\n=== SH GET ==="); + Serial.println("Base: " + base); + Serial.println("Path: " + path); + + at("AT+SHDISC", 2000); // clear any stale session (ignore CME 3) + at("AT+SHCHEAD", 2000); // clear headers + + + at("AT+SHCONF=\"URL\",\"" + base + "\"", 4000); + //at("AT+SHCONF=\"BODYLEN\",1024", 2000); + //at("AT+SHCONF=\"HEADERLEN\",350", 2000); + + String r = at("AT+SHCONN", 12000); + + Serial.println("---- CONNECTION !!----"); + Serial.println(r); + + if (r.indexOf("OK")==-1) { Serial.println("SHCONN failed"); return; } + + at("AT+SHCHEAD", 2000); + // Optionally set UA if your server cares: + //at("AT+SHAHEAD=\"User-Agent\",\"SIM7080G\"",2000); + + at("AT+SHAHEAD=\"User-Agent\",\"IOE Client\"",2000); + // at("AT+SHAHEAD=\"User-Agent\",\"curl/7.47.0\"",2000); + at("AT+SHAHEAD=\"Cache-control\",\"no-cache\"",2000); + at("AT+SHAHEAD=\"Connection\",\"keep-alive\"",2000); + + // at("AT+SHAHEAD=\"Content-Type\",\"application/json\"",2000); + + // at("AT+SHAHEAD=\"Content-Type\",\"text/html\"",2000); + at("AT+SHAHEAD=\"Accept\",\"text/html,*/*\"",2000); + //at("AT+SHAHEAD=\"Accept-encoding\",\"gzip, deflate\"",2000); + + String u = at("AT+SHREQ=\"" +path + "\",1", 20000); // 1 = GET + + + Serial.println("---- REQUESTED !!----"); + Serial.println(u); + + + + + //String u = at("AT+SHREQ=\"" + path + "\",1", 20000); +int bodyLen = parseLenFromSHREQ(u); +Serial.print("Parsed body length: "); Serial.println(bodyLen); + + +if (bodyLen > 0) { + String body = at("AT+SHREAD=0," + String(bodyLen), 5000); + Serial.println("---- SHREAD(N) RAW ----"); + Serial.println(body); +} + + + // First read + + //String body0 = at("AT+SHREAD=0,8", 3000); + //Serial.println("---- SHREAD(8) RAW ----"); + //Serial.println(body0); + + + + //String body1 = at("AT+SHREAD=0,2048", 4000); + //Serial.println("01 ---- SHREAD(2048) RAW ----"); + //Serial.println(body1); + // Second smaller read + //String body2 = at("AT+SHREAD=0,512", 3000); + //Serial.println("---- SHREAD(512) RAW ----"); + //Serial.println(body2); + + + //String body3 = at("AT+SHREAD=0,20", 3000); + //Serial.println("---- SHREAD(20) RAW ----"); + //Serial.println(body3); + + + + + at("AT+SHDISC", 2000); +} + +void setup() { + Serial.begin(115200); + Serial2.begin(9600, SERIAL_8N1, RXD2, TXD2); + + powerOnSIM7080(); + attachPDP(); + + // 1) Your Laravel endpoint + shGet("http://laravel-server.lab.audasmedia.com.au", + "/api/device/860016049744324/commands"); + + +//shGet("http://httpforever.com/","/"); + + // 2) Example.com root + // shGet("http://example.com", "/"); + + // 3) Adafruit WiFi test page +// shGet("http://wifitest.adafruit.com", "/testwifi/index.html"); + + // 4) httpbin GET +// shGet("http://httpbin.org", "/get"); + + Serial.println("\n--- All GETs attempted ---"); +} + + + + +void loop() { + delay(1000); +} diff --git a/src/core/CommandMgr.cpp b/src/core/CommandMgr.cpp index 4da3b13..8d1da96 100644 --- a/src/core/CommandMgr.cpp +++ b/src/core/CommandMgr.cpp @@ -125,7 +125,16 @@ void poll(uint32_t minIntervalMs) { String path = "/api/device/" + IMEI + "/commands"; String body; - bool ok = HttpClient::getJson(BASE, path, body); + + +String fullUrl = BASE + path; // e.g., "http://laravel-server.lab.audasmedia.com.au" + "/api/device/IMEI/commands" + +//bool ok =HttpClient::getJsonToFile(fullUrl, body); + + bool ok = HttpClient::getJsonExact(BASE, path, body); + Serial.println("Commands JSON: " + body); + // TODO: replace with proper JSON parsing later +// Crude parse: split by "},{" for now and extract id/type/payload as before. if (!ok || body.length()==0) return; std::vector cmds; @@ -155,6 +164,8 @@ void poll(uint32_t minIntervalMs) { bool handleLights(const String& payload) { // TODO: parse {"on":true} and toggle GPIO via your GpioCtrl later Serial.println("handleLights payload: " + payload); + bool on = (payload.indexOf("\"on\":true") != -1); + GpioCtrl::setLight(on); return true; } @@ -170,4 +181,4 @@ bool handleRingFence(const String& payload) { return true; } -} // namespace CommandMgr \ No newline at end of file +// } // namespace CommandMgr \ No newline at end of file diff --git a/src/core/HttpClient.cpp b/src/core/HttpClient.cpp index c162052..977382e 100644 --- a/src/core/HttpClient.cpp +++ b/src/core/HttpClient.cpp @@ -1,69 +1,93 @@ #include "HttpClient.hpp" #include "NetMgr.hpp" -namespace { - -bool ensureHttpReady(const String& baseUrl) { - NetMgr::sendAT("AT+SHCONF=\"URL\",\"" + baseUrl + "\"", 5000); - NetMgr::sendAT("AT+SHCONF=\"BODYLEN\",1024", 2000); - NetMgr::sendAT("AT+SHCONF=\"HEADERLEN\",350", 2000); - String r = NetMgr::sendAT("AT+SHCONN", 15000); - if (r.indexOf("OK") == -1) return false; - NetMgr::sendAT("AT+SHCHEAD", 2000); - return true; -} - -String readAll(uint32_t chunk = 2048) { - // Try to read body; some servers may close early (CME 3). That’s OK. - String b = NetMgr::sendAT("AT+SHREAD=0," + String(chunk), 4000); - return b; -} - -} // anon - namespace HttpClient { bool postJson(const String& baseUrl, const String& path, const String& jsonBody) { - if (!ensureHttpReady(baseUrl)) { Serial.println("SHCONN failed"); return false; } + NetMgr::sendAT("AT+SHDISC", 2000); + NetMgr::sendAT("AT+SHCHEAD", 2000); + + if (NetMgr::sendAT("AT+SHCONF=\"URL\",\"" + baseUrl + "\"", 4000).indexOf("OK") == -1) + return false; + + String r = NetMgr::sendAT("AT+SHCONN", 12000); + if (r.indexOf("OK") == -1) return false; + + NetMgr::sendAT("AT+SHCHEAD", 2000); NetMgr::sendAT("AT+SHAHEAD=\"Content-Type\",\"application/json\"", 2000); NetMgr::sendAT("AT+SHBOD=" + String(jsonBody.length()) + ",10000", 2000); Serial2.print(jsonBody); delay(200); - String r = NetMgr::sendAT("AT+SHREQ=\"" + path + "\",3", 20000); // 3=POST + r = NetMgr::sendAT("AT+SHREQ=\"" + path + "\",3", 20000); // 3 = POST int p = r.indexOf("+SHREQ:"); - if (p != -1) Serial.println("Status/URC: " + r.substring(p)); - readAll(); - NetMgr::sendAT("AT+SHDISC", 3000); + if (p != -1) Serial.println("POST status URC: " + r.substring(p)); + + // optional read (non-fatal) + NetMgr::sendAT("AT+SHREAD=0,2048", 4000); + + NetMgr::sendAT("AT+SHDISC", 2000); return true; } -bool getJson(const String& baseUrl, const String& path, String& outBody) { +bool getJsonExact(const String& baseUrl, const String& path, String& outBody) { outBody = ""; - if (!ensureHttpReady(baseUrl)) return false; - String r = NetMgr::sendAT("AT+SHREQ=\"" + path + "\",1", 20000); // 1=GET - // Expect +SHREQ: "GET",, - int s = r.indexOf("+SHREQ:"); - if (s != -1) Serial.println("Status/URC: " + r.substring(s)); + NetMgr::sendAT("AT+SHDISC", 2000); + NetMgr::sendAT("AT+SHCHEAD", 2000); - // Read response (best effort) - String raw = readAll(); - // raw looks like: - // +SHREAD: - // - // Parse body after the first newline following +SHREAD: - int tag = raw.indexOf("+SHREAD:"); - if (tag != -1) { - int nl = raw.indexOf('\n', tag); - if (nl != -1 && nl + 1 < (int)raw.length()) { - outBody = raw.substring(nl + 1); - outBody.trim(); + if (NetMgr::sendAT("AT+SHCONF=\"URL\",\"" + baseUrl + "\"", 4000).indexOf("OK") == -1) + return false; + + String r = NetMgr::sendAT("AT+SHCONN", 12000); + if (r.indexOf("OK") == -1) return false; + + NetMgr::sendAT("AT+SHCHEAD", 2000); + NetMgr::sendAT("AT+SHAHEAD=\"Connection\",\"keep-alive\"", 2000); + NetMgr::sendAT("AT+SHAHEAD=\"Accept\",\"application/json\"", 2000); + + // GET + parse length + String urc = NetMgr::sendAT("AT+SHREQ=\"" + path + "\",1", 20000); + + // parse len from URC + int len = -1; + { + int start = 0; + while (start < (int)urc.length()) { + int end = urc.indexOf('\n', start); + if (end == -1) end = urc.length(); + String line = urc.substring(start, end); + line.trim(); + if (line.startsWith("+SHREQ:")) { + int lastComma = line.lastIndexOf(','); + if (lastComma >= 0) { + int i = lastComma + 1; + while (i < (int)line.length() && line[i]==' ') i++; + int j = i; + while (j < (int)line.length() && isDigit(line[j])) j++; + if (j > i) len = line.substring(i, j).toInt(); + } + break; + } + start = end + 1; } } - NetMgr::sendAT("AT+SHDISC", 3000); - return true; + + if (len > 0) { + String raw = NetMgr::sendAT("AT+SHREAD=0," + String(len), 6000); + int nl = raw.indexOf('\n'); + if (nl != -1) { + String b = raw.substring(nl + 1); + int okPos = b.lastIndexOf("\nOK"); + if (okPos >= 0) b = b.substring(0, okPos); + b.trim(); + outBody = b; + } + } + + NetMgr::sendAT("AT+SHDISC", 2000); + return outBody.length() > 0; } } // namespace HttpClient \ No newline at end of file diff --git a/src/core/HttpClient.hpp b/src/core/HttpClient.hpp index f686379..b638045 100644 --- a/src/core/HttpClient.hpp +++ b/src/core/HttpClient.hpp @@ -3,5 +3,5 @@ namespace HttpClient { bool postJson(const String& baseUrl, const String& path, const String& jsonBody); - bool getJson(const String& baseUrl, const String& path, String& outBody); // NEW + bool getJsonExact(const String& baseUrl, const String& path, String& outBody); } \ No newline at end of file diff --git a/src/core/Telemetry.cpp b/src/core/Telemetry.cpp index e69de29..f1f6e9f 100644 --- a/src/core/Telemetry.cpp +++ b/src/core/Telemetry.cpp @@ -0,0 +1,48 @@ +#include "Telemetry.hpp" +#include "NetMgr.hpp" + +namespace Telemetry { + +bool readGNSS(double& lat, double& lng, double& alt, double& speed, double& heading) { + lat=lng=alt=speed=heading=0; + String r = NetMgr::sendAT("AT+CGNSINF", 3000); + // +CGNSINF: 1,1,yyyyMMddhhmmss.sss,lat,lng,alt, ... ,speed_over_ground, ... ,course + int p = r.indexOf("+CGNSINF:"); + if (p < 0) return false; + // crude split + int first = r.indexOf('\n', p); // skip header if present + String line = (first!=-1) ? r.substring(p, first) : r.substring(p); + // safer: take last line + int lastNl = r.lastIndexOf('\n'); + line = (lastNl!=-1) ? r.substring(lastNl+1) : line; + line.trim(); + + // tokenization + int commas[10]; int count=0; + for (int i=0;i<(int)line.length() && count<10;i++) if (line[i]==',') commas[count++]=i; + if (count < 6) return false; + + lat = line.substring(commas[1]+1, commas[2]).toDouble(); + lng = line.substring(commas[2]+1, commas[3]).toDouble(); + alt = line.substring(commas[3]+1, commas[4]).toDouble(); + // speed (km/h) field may be later; do a find: + int next = line.indexOf(',', commas[4]+1); + next = line.indexOf(',', next+1); + next = line.indexOf(',', next+1); + int speedStart = next+1; + int speedEnd = line.indexOf(',', speedStart); + if (speedStart>0 && speedEnd>speedStart) speed = line.substring(speedStart, speedEnd).toDouble(); + // heading/course similarly if needed + return true; +} + +String buildJson(const String& imei, double lat, double lng, double alt, double speed, double heading) { + String j = String("{\"device_id\":\"") + imei + "\"," + + "\"lat\":" + String(lat,6) + "," + + "\"lng\":" + String(lng,6) + "," + + "\"speed\":" + String(speed,2) + "," + + "\"altitude\":" + String(alt,2) + "}"; + return j; +} + +} // namespace Telemetry \ No newline at end of file diff --git a/src/core/Telemetry.hpp b/src/core/Telemetry.hpp index e69de29..dfb1529 100644 --- a/src/core/Telemetry.hpp +++ b/src/core/Telemetry.hpp @@ -0,0 +1,6 @@ +#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); +} \ No newline at end of file diff --git a/src/hw/GpioCtrl.cpp b/src/hw/GpioCtrl.cpp index e69de29..42a9de0 100644 --- a/src/hw/GpioCtrl.cpp +++ b/src/hw/GpioCtrl.cpp @@ -0,0 +1,8 @@ +#include "GpioCtrl.hpp" +namespace { + uint8_t lp = 255; +} +namespace GpioCtrl { + void init(uint8_t lightPin) { lp = lightPin; pinMode(lp, OUTPUT); digitalWrite(lp, LOW); } + void setLight(bool on) { if (lp != 255) digitalWrite(lp, on ? HIGH : LOW); } +} \ No newline at end of file diff --git a/src/hw/GpioCtrl.hpp b/src/hw/GpioCtrl.hpp index e69de29..f5e7f13 100644 --- a/src/hw/GpioCtrl.hpp +++ b/src/hw/GpioCtrl.hpp @@ -0,0 +1,6 @@ +#pragma once +#include +namespace GpioCtrl { + void init(uint8_t lightPin); + void setLight(bool on); +} \ No newline at end of file