Modular PR1: NetMgr, HttpClient, SmsMgr with working attach/HTTP/SMS
This commit is contained in:
@ -1,9 +1,26 @@
|
|||||||
#include "app/App.hpp"
|
#include "src/core/NetMgr.hpp"
|
||||||
|
#include "src/core/HttpClient.hpp"
|
||||||
|
#include "src/core/SmsMgr.hpp"
|
||||||
|
|
||||||
|
static const char* APN = "hologram";
|
||||||
|
static const char* BASE_URL = "http://laravel-server.lab.audasmedia.com.au";
|
||||||
|
static const char* PATH = "/api/gps";
|
||||||
|
|
||||||
void setup() {
|
void setup() {
|
||||||
App::instance().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
|
||||||
|
|
||||||
|
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() {
|
void loop() {
|
||||||
App::instance().loop();
|
SmsMgr::pollUrc(); // non‑blocking URC scan; handles +CMTI
|
||||||
|
SmsMgr::pollUnread(); // optional safety poll
|
||||||
|
delay(250);
|
||||||
}
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
#include "App.hpp"
|
#include "App.hpp"
|
||||||
|
#include "../core/HttpClient.hpp"
|
||||||
|
|
||||||
App& App::instance() {
|
App& App::instance() {
|
||||||
static App inst;
|
static App inst;
|
||||||
|
|||||||
@ -0,0 +1,34 @@
|
|||||||
|
#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);
|
||||||
|
NetMgr::sendAT("AT+SHAHEAD=\"Content-Type\",\"application/json\"", 2000);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
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+SHBOD=" + String(jsonBody.length()) + ",10000", 2000);
|
||||||
|
Serial2.print(jsonBody);
|
||||||
|
delay(200);
|
||||||
|
String 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));
|
||||||
|
NetMgr::sendAT("AT+SHREAD=0,2048", 4000); // non-fatal if CME 3
|
||||||
|
NetMgr::sendAT("AT+SHDISC", 3000);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace HttpClient
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
namespace HttpClient {
|
||||||
|
bool postJson(const String& baseUrl, const String& path, const String& jsonBody);
|
||||||
|
}
|
||||||
@ -0,0 +1,61 @@
|
|||||||
|
#include "NetMgr.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
String at(const String& cmd, uint32_t timeoutMs) {
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
resp.trim();
|
||||||
|
Serial.println("\n[Resp]\n" + resp);
|
||||||
|
return resp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace NetMgr {
|
||||||
|
|
||||||
|
String sendAT(const String& cmd, uint32_t timeoutMs) { return at(cmd, timeoutMs); }
|
||||||
|
|
||||||
|
void powerOnSIM7080(uint8_t pwrkeyPin) {
|
||||||
|
pinMode(pwrkeyPin, OUTPUT);
|
||||||
|
digitalWrite(pwrkeyPin, LOW); delay(2000);
|
||||||
|
digitalWrite(pwrkeyPin, HIGH); delay(1000);
|
||||||
|
digitalWrite(pwrkeyPin, LOW); delay(30000);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool waitLTE(uint32_t ms) {
|
||||||
|
uint32_t t0 = millis();
|
||||||
|
while (millis() - t0 < ms) {
|
||||||
|
String r = at("AT+CEREG?", 4000);
|
||||||
|
if (r.indexOf("+CEREG: 0,1") != -1 || r.indexOf("+CEREG: 0,5") != -1) return true;
|
||||||
|
delay(3000);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void attachAndPdp(const char* apn) {
|
||||||
|
at("AT", 2000);
|
||||||
|
at("AT+CMEE=1", 2000);
|
||||||
|
at("AT+CPIN?", 2000);
|
||||||
|
at("AT+CSQ", 2000);
|
||||||
|
at("AT+CNMP=38", 5000);
|
||||||
|
at("AT+CMNB=1", 5000);
|
||||||
|
at("AT+CGDCONT=1,\"IP\",\"" + String(apn) + "\"", 5000);
|
||||||
|
at("AT+CGATT=1", 10000);
|
||||||
|
|
||||||
|
if (!waitLTE()) { Serial.println("No LTE reg"); while (true) delay(1000); }
|
||||||
|
|
||||||
|
at("AT+CNCFG=0,1,\"" + String(apn) + "\"", 5000);
|
||||||
|
at("AT+CNACT=0,1", 45000); // may 767 if already active
|
||||||
|
at("AT+CNACT?", 3000);
|
||||||
|
at("AT+CDNSCFG=\"8.8.8.8\",\"1.1.1.1\"", 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace NetMgr
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
namespace NetMgr {
|
||||||
|
void powerOnSIM7080(uint8_t pwrkeyPin);
|
||||||
|
bool waitLTE(uint32_t ms = 120000);
|
||||||
|
void attachAndPdp(const char* apn);
|
||||||
|
String sendAT(const String& cmd, uint32_t timeoutMs);
|
||||||
|
}
|
||||||
@ -0,0 +1,89 @@
|
|||||||
|
#include "SmsMgr.hpp"
|
||||||
|
#include "NetMgr.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
String readAndDelete(int idx) {
|
||||||
|
String r = NetMgr::sendAT("AT+CMGR=" + String(idx), 4000);
|
||||||
|
int okPos = r.lastIndexOf("\nOK");
|
||||||
|
String s = (okPos>0) ? r.substring(0, okPos) : r;
|
||||||
|
int lastLf = s.lastIndexOf('\n');
|
||||||
|
if (lastLf != -1) s = s.substring(lastLf + 1);
|
||||||
|
s.trim();
|
||||||
|
NetMgr::sendAT("AT+CMGD=" + String(idx), 2000);
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
void parseSimpleJson(const String& msg) {
|
||||||
|
auto extract = [&](const String& key)->String {
|
||||||
|
String pat = "\"" + key + "\":";
|
||||||
|
int k = msg.indexOf(pat);
|
||||||
|
if (k < 0) return "";
|
||||||
|
k += pat.length();
|
||||||
|
while (k < (int)msg.length() && (msg[k]==' '||msg[k]=='\"')) k++;
|
||||||
|
if (k>0 && msg[k-1]=='\"') {
|
||||||
|
int e = msg.indexOf('\"', k);
|
||||||
|
return (e>k) ? msg.substring(k, e) : "";
|
||||||
|
} else {
|
||||||
|
int e=k; while (e<(int)msg.length() && (isDigit(msg[e])||msg[e]=='.'||msg[e]=='-')) e++;
|
||||||
|
return msg.substring(k, e);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
String deviceId = extract("device_id");
|
||||||
|
String speed = extract("speed");
|
||||||
|
Serial.println("SMS JSON parsed: device_id=" + deviceId + " speed=" + speed);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // anon
|
||||||
|
|
||||||
|
namespace SmsMgr {
|
||||||
|
|
||||||
|
void setup() {
|
||||||
|
NetMgr::sendAT("AT+CMGF=1", 2000);
|
||||||
|
NetMgr::sendAT("AT+CPMS=\"SM\",\"SM\",\"SM\"", 2000);
|
||||||
|
NetMgr::sendAT("AT+CNMI=2,1,0,0,0", 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
void handleBody(const String& body) {
|
||||||
|
Serial.println("SMS body: " + body);
|
||||||
|
if (body.startsWith("{")) parseSimpleJson(body);
|
||||||
|
// else map plain-text commands here
|
||||||
|
}
|
||||||
|
|
||||||
|
void pollUrc() {
|
||||||
|
static String urc;
|
||||||
|
while (Serial2.available()) {
|
||||||
|
char c = (char)Serial2.read();
|
||||||
|
urc += c;
|
||||||
|
if (c=='\n') {
|
||||||
|
urc.trim();
|
||||||
|
if (urc.startsWith("+CMTI:")) {
|
||||||
|
int comma = urc.lastIndexOf(',');
|
||||||
|
if (comma != -1) {
|
||||||
|
int idx = urc.substring(comma+1).toInt();
|
||||||
|
String body = readAndDelete(idx);
|
||||||
|
handleBody(body);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
urc = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void pollUnread() {
|
||||||
|
String list = NetMgr::sendAT("AT+CMGL=\"REC UNREAD\"", 3000);
|
||||||
|
int pos = 0;
|
||||||
|
while (true) {
|
||||||
|
int p = list.indexOf("+CMGL:", pos);
|
||||||
|
if (p == -1) break;
|
||||||
|
int comma = list.indexOf(',', p);
|
||||||
|
int idxStart = list.indexOf(' ', p);
|
||||||
|
if (comma==-1 || idxStart==-1 || idxStart>comma) break;
|
||||||
|
int idx = list.substring(idxStart+1, comma).toInt();
|
||||||
|
String body = readAndDelete(idx);
|
||||||
|
handleBody(body);
|
||||||
|
pos = comma + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace SmsMgr
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
namespace SmsMgr {
|
||||||
|
void setup();
|
||||||
|
void pollUrc(); // handle +CMTI
|
||||||
|
void pollUnread(); // optional periodic safety poll
|
||||||
|
void handleBody(const String& body); // your command handler entry
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user