Initial monolithic sketch (backup included)
This commit is contained in:
3
.vscode/settings.json
vendored
3
.vscode/settings.json
vendored
@ -1,6 +1,7 @@
|
||||
{
|
||||
"idf.pythonInstallPath": "/usr/bin/python3",
|
||||
"files.associations": {
|
||||
"string": "cpp"
|
||||
"string": "cpp",
|
||||
"ostream": "cpp"
|
||||
}
|
||||
}
|
||||
@ -1,8 +0,0 @@
|
||||
{
|
||||
"folders": [
|
||||
{
|
||||
"path": "."
|
||||
}
|
||||
],
|
||||
"settings": {}
|
||||
}
|
||||
@ -1,275 +1,260 @@
|
||||
#include <ArduinoJson.h>
|
||||
#include <HardwareSerial.h>
|
||||
|
||||
// --- SIM7080G Pin Definitions ---
|
||||
#define LTE_RX_PIN 16 // ESP32 GPIO16 connected to SIM7080G TX
|
||||
#define LTE_TX_PIN 17 // ESP32 GPIO17 connected to SIM7080G RX
|
||||
#define PWRKEY_PIN 4 // ESP32 GPIO4 connected to SIM7080G PWRKEY
|
||||
// Pins
|
||||
#define LTE_RX_PIN 16 // ESP32 RX <- SIM7080 TX
|
||||
#define LTE_TX_PIN 17 // ESP32 TX -> SIM7080 RX
|
||||
#define PWRKEY_PIN 4 // SIM7080 PWRKEY
|
||||
|
||||
// --- Network Configuration ---
|
||||
const char *HOLOGRAM_APN = "hologram";
|
||||
// Network and endpoint
|
||||
const char* APN = "hologram";
|
||||
const char* BASE_URL = "http://laravel-server.lab.audasmedia.com.au";
|
||||
const char* PATH = "/api/gps";
|
||||
|
||||
// --- Global Variables ---
|
||||
String currentIMEI = "";
|
||||
// -------- Utilities --------
|
||||
String sendAT(const String& cmd, uint32_t timeoutMs) {
|
||||
Serial.print(">> "); Serial.println(cmd);
|
||||
while (Serial2.available()) (void)Serial2.read();
|
||||
Serial2.println(cmd);
|
||||
|
||||
// --- Function Prototypes ---
|
||||
void powerOnModule();
|
||||
void powerOffModule();
|
||||
String sendATCommand(String command, unsigned long timeoutMs = 3000);
|
||||
String getIMEI();
|
||||
bool activatePDPContextAndBearer();
|
||||
void getGpsLocation(); // Function to get and print GPS data
|
||||
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 timeout on input
|
||||
}
|
||||
}
|
||||
resp.trim();
|
||||
Serial.println("\n[Resp]\n" + resp);
|
||||
return resp;
|
||||
}
|
||||
|
||||
void powerOnSIM7080() {
|
||||
pinMode(PWRKEY_PIN, OUTPUT);
|
||||
digitalWrite(PWRKEY_PIN, LOW);
|
||||
delay(2000);
|
||||
digitalWrite(PWRKEY_PIN, HIGH);
|
||||
delay(1000);
|
||||
digitalWrite(PWRKEY_PIN, LOW);
|
||||
delay(30000); // full boot
|
||||
}
|
||||
|
||||
bool waitLTE(uint32_t ms = 120000) {
|
||||
uint32_t t0 = millis();
|
||||
while (millis() - t0 < ms) {
|
||||
String r = sendAT("AT+CEREG?", 4000);
|
||||
if (r.indexOf("+CEREG: 0,1") != -1 || r.indexOf("+CEREG: 0,5") != -1) return true;
|
||||
delay(3000);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Simple reusable HTTP POST via SH client
|
||||
bool httpPostSH(const String& baseUrl, const String& path, const String& jsonBody) {
|
||||
// Configure HTTP client
|
||||
sendAT("AT+SHCONF=\"URL\",\"" + baseUrl + "\"", 5000);
|
||||
sendAT("AT+SHCONF=\"BODYLEN\",1024", 2000);
|
||||
sendAT("AT+SHCONF=\"HEADERLEN\",350", 2000);
|
||||
|
||||
// Connect (no micromanagement; rely on OK)
|
||||
String r = sendAT("AT+SHCONN", 15000);
|
||||
if (r.indexOf("OK") == -1) {
|
||||
Serial.println("SHCONN failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clear and set headers
|
||||
sendAT("AT+SHCHEAD", 2000);
|
||||
sendAT("AT+SHAHEAD=\"Content-Type\",\"application/json\"", 2000);
|
||||
|
||||
// Send body
|
||||
String cmd = "AT+SHBOD=" + String(jsonBody.length()) + ",10000";
|
||||
sendAT(cmd, 2000);
|
||||
Serial2.print(jsonBody);
|
||||
delay(200);
|
||||
|
||||
// POST (3 = POST)
|
||||
r = sendAT("AT+SHREQ=\"" + path + "\",3", 20000);
|
||||
// Expect URC like: +SHREQ: "POST",<HTTP_CODE>,<len>
|
||||
// Print status line if present
|
||||
int p = r.indexOf("+SHREQ:");
|
||||
if (p != -1) {
|
||||
Serial.println("Status/URC: " + r.substring(p));
|
||||
}
|
||||
|
||||
// If there is a body, try to read (non-fatal if none)
|
||||
sendAT("AT+SHREAD=0,2048", 4000);
|
||||
|
||||
// Disconnect
|
||||
sendAT("AT+SHDISC", 3000);
|
||||
return true;
|
||||
}
|
||||
|
||||
// -------- Arduino setup/loop --------
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
while (!Serial) {
|
||||
delay(10);
|
||||
}
|
||||
Serial.println("--- Starting ESP32 to SIM7080G GPS Test (Final) ---");
|
||||
Serial2.begin(9600, SERIAL_8N1, LTE_RX_PIN, LTE_TX_PIN);
|
||||
Serial.println("--- SIM7080: Simple Connect + HTTP POST ---");
|
||||
|
||||
Serial2.begin(115200, SERIAL_8N1, LTE_RX_PIN, LTE_TX_PIN);
|
||||
powerOnSIM7080();
|
||||
|
||||
powerOffModule();
|
||||
delay(3000);
|
||||
powerOnModule();
|
||||
// Basic checks
|
||||
sendAT("AT", 2000);
|
||||
sendAT("AT+CMEE=1", 2000);
|
||||
sendAT("AT+CPIN?", 2000);
|
||||
sendAT("AT+CSQ", 2000);
|
||||
|
||||
// Step 1: "Nudge and Listen" for the initial boot-up.
|
||||
Serial.println(
|
||||
"Waiting for initial module boot (90s timeout)...");
|
||||
unsigned long startTime = millis();
|
||||
unsigned long lastNudgeTime = 0;
|
||||
const unsigned long NUDGE_INTERVAL = 3000;
|
||||
bool moduleBooted = false;
|
||||
String bootResponse = "";
|
||||
// Force Cat‑M1 + APN attach
|
||||
sendAT("AT+CNMP=38", 5000);
|
||||
sendAT("AT+CMNB=1", 5000);
|
||||
sendAT("AT+CGDCONT=1,\"IP\",\"" + String(APN) + "\"", 5000);
|
||||
sendAT("AT+CGATT=1", 10000);
|
||||
|
||||
while (millis() - startTime < 90000 && !moduleBooted) {
|
||||
while (Serial2.available()) {
|
||||
char c = Serial2.read();
|
||||
Serial.write(c);
|
||||
bootResponse += c;
|
||||
if (bootResponse.indexOf("SMS Ready") != -1) {
|
||||
moduleBooted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (moduleBooted) {
|
||||
break;
|
||||
}
|
||||
if (millis() - lastNudgeTime > NUDGE_INTERVAL) {
|
||||
Serial2.println("AT");
|
||||
lastNudgeTime = millis();
|
||||
}
|
||||
// Wait for LTE registration
|
||||
if (!waitLTE()) {
|
||||
Serial.println("No LTE registration; stopping.");
|
||||
while (true) delay(1000);
|
||||
}
|
||||
|
||||
if (!moduleBooted) {
|
||||
Serial.println("Module did not complete initial boot. Halting.");
|
||||
while (true)
|
||||
;
|
||||
}
|
||||
Serial.println("\nInitial boot successful!");
|
||||
// Activate SIM7080 APP PDP (required for HTTP)
|
||||
sendAT("AT+CNCFG=0,1,\"" + String(APN) + "\"", 5000);
|
||||
sendAT("AT+CNACT=0,1", 45000);
|
||||
sendAT("AT+CNACT?", 3000);
|
||||
|
||||
// Step 2: Apply the proven network configuration from your manual test.
|
||||
Serial.println("\nApplying network configuration...");
|
||||
sendATCommand("AT+CMEE=1", 1000);
|
||||
sendATCommand("AT+CNMP=38", 5000);
|
||||
sendATCommand("AT+CMNB=3", 5000);
|
||||
smsSetup();
|
||||
|
||||
// Step 3: Perform the full CFUN reset cycle to apply settings.
|
||||
Serial.println("Applying settings with full CFUN reset...");
|
||||
sendATCommand("AT+CFUN=0", 5000);
|
||||
delay(5000);
|
||||
sendATCommand("AT+CFUN=1", 10000);
|
||||
// Optional DNS
|
||||
sendAT("AT+CDNSCFG=\"8.8.8.8\",\"1.1.1.1\"", 3000);
|
||||
|
||||
// Step 4: Wait for the modem to apply settings and find the network.
|
||||
Serial.println(
|
||||
"Waiting 45 seconds for modem to apply settings and find network...");
|
||||
delay(45000);
|
||||
// Build your JSON (adjust as needed)
|
||||
String json = "{\"device_id\":\"sim7080g-01\",\"lat\":-33.865143,"
|
||||
"\"lng\":151.2099,\"speed\":12.5,\"altitude\":30.2}";
|
||||
|
||||
Serial.println("\nChecking network status...");
|
||||
currentIMEI = getIMEI();
|
||||
// Do the POST
|
||||
bool ok = httpPostSH(BASE_URL, PATH, json);
|
||||
Serial.println(ok ? "HTTP POST sent (see status above)" : "HTTP POST failed");
|
||||
|
||||
// Step 5: Check for network registration.
|
||||
unsigned long networkRegStartTime = millis();
|
||||
bool networkRegistered = false;
|
||||
while (millis() - networkRegStartTime < 120000 && !networkRegistered) {
|
||||
String regResponse = sendATCommand("AT+CGREG?", 3000);
|
||||
if (regResponse.indexOf("+CGREG: 0,1") != -1 ||
|
||||
regResponse.indexOf("+CGREG: 0,5") != -1) {
|
||||
networkRegistered = true;
|
||||
}
|
||||
if (networkRegistered) {
|
||||
Serial.println("Network registration successful!");
|
||||
break;
|
||||
}
|
||||
Serial.println("Not yet registered. Retrying...");
|
||||
delay(5000);
|
||||
}
|
||||
|
||||
if (!networkRegistered) {
|
||||
Serial.println("Failed to register to network. Halting.");
|
||||
while (true)
|
||||
;
|
||||
}
|
||||
|
||||
// --- GPS INITIALIZATION ---
|
||||
Serial.println("\n--- Turning on GPS ---");
|
||||
sendATCommand("AT+CGPS=1", 1000); // Turn on the GPS receiver
|
||||
Serial.println(
|
||||
"GPS is on. It may take several minutes to get the first fix.");
|
||||
|
||||
Serial.println("\n--- Setup complete. Starting main loop. ---");
|
||||
Serial.println("--- Done ---");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
Serial.println("\n--- Requesting GPS Location ---");
|
||||
getGpsLocation();
|
||||
delay(30000); // Wait 30 seconds before the next request
|
||||
// Non-blocking read to capture unsolicited lines like +CMTI
|
||||
static String urc;
|
||||
while (Serial2.available()) {
|
||||
char c = (char)Serial2.read();
|
||||
urc += c;
|
||||
if (c == '\n') {
|
||||
urc.trim();
|
||||
if (urc.startsWith("+CMTI:")) {
|
||||
// +CMTI: "SM",<idx>
|
||||
int comma = urc.lastIndexOf(',');
|
||||
if (comma != -1) {
|
||||
int idx = urc.substring(comma + 1).toInt();
|
||||
String body = smsReadAndDelete(idx);
|
||||
Serial.println("SMS (URC): " + body);
|
||||
handleSmsCommand(body);
|
||||
}
|
||||
}
|
||||
urc = "";
|
||||
}
|
||||
}
|
||||
|
||||
void getGpsLocation() {
|
||||
String response = sendATCommand("AT+CGPSINFO", 5000);
|
||||
// Optional periodic poll in case URC was missed
|
||||
static uint32_t lastPoll = 0;
|
||||
if (millis() - lastPoll > 60000) { // every 60 s
|
||||
smsPollUnread();
|
||||
lastPoll = millis();
|
||||
}
|
||||
|
||||
if (response.indexOf("+CGPSINFO:") != -1) {
|
||||
response.replace("+CGPSINFO: ", "");
|
||||
response.trim();
|
||||
// Your normal work here...
|
||||
}
|
||||
|
||||
if (response.startsWith(",")) {
|
||||
Serial.println("GPS has no fix yet.");
|
||||
// Call once after PDP is active (or right after registration)
|
||||
void smsSetup() {
|
||||
sendAT("AT+CMGF=1", 2000); // text mode
|
||||
sendAT("AT+CPMS=\"SM\",\"SM\",\"SM\"", 2000); // SIM storage
|
||||
sendAT("AT+CNMI=2,1,0,0,0", 2000); // new SMS URC: +CMTI: "SM",<idx>
|
||||
// Optional: show extended text params
|
||||
// sendAT("AT+CSDH=1", 2000);
|
||||
}
|
||||
|
||||
// Read & delete one SMS by index; returns the body
|
||||
String smsReadAndDelete(int idx) {
|
||||
String r = sendAT("AT+CMGR=" + String(idx), 4000);
|
||||
// Extract last non-empty line before OK
|
||||
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();
|
||||
|
||||
sendAT("AT+CMGD=" + String(idx), 2000);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Optional: poll unread SMS (if you want to scan periodically)
|
||||
void smsPollUnread() {
|
||||
String list = sendAT("AT+CMGL=\"REC UNREAD\"", 5000);
|
||||
// list may contain many entries like:
|
||||
// +CMGL: <idx>,"REC UNREAD",...
|
||||
int pos = 0;
|
||||
while (true) {
|
||||
int p = list.indexOf("+CMGL:", pos);
|
||||
if (p == -1) break;
|
||||
int comma = list.indexOf(',', p);
|
||||
if (comma == -1) break;
|
||||
// Extract index after "+CMGL: "
|
||||
int idxStart = list.indexOf(' ', p);
|
||||
if (idxStart == -1 || idxStart > comma) break;
|
||||
int idx = list.substring(idxStart + 1, comma).toInt();
|
||||
String body = smsReadAndDelete(idx);
|
||||
Serial.println("SMS (polled): " + body);
|
||||
handleSmsCommand(body);
|
||||
pos = comma + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void handleSmsCommand(String msg) {
|
||||
Serial.println("Command received via SMS: " + msg);
|
||||
|
||||
msg.trim();
|
||||
if (msg.isEmpty()) {
|
||||
Serial.println("Empty body");
|
||||
return;
|
||||
}
|
||||
|
||||
int firstComma = response.indexOf(',');
|
||||
String latDDMM = response.substring(0, firstComma);
|
||||
int secondComma = response.indexOf(',', firstComma + 1);
|
||||
String nsIndicator = response.substring(firstComma + 1, secondComma);
|
||||
int thirdComma = response.indexOf(',', secondComma + 1);
|
||||
String lonDDMM = response.substring(secondComma + 1, thirdComma);
|
||||
int fourthComma = response.indexOf(',', thirdComma + 1);
|
||||
String ewIndicator = response.substring(thirdComma + 1, fourthComma);
|
||||
// Very simple JSON extraction: look for device_id and speed keys
|
||||
auto extract = [&](const String& key)->String {
|
||||
String pat = String("\"") + key + "\":";
|
||||
int k = msg.indexOf(pat);
|
||||
if (k < 0) return "";
|
||||
k += pat.length();
|
||||
|
||||
double latitude = 0.0;
|
||||
if (latDDMM.length() > 2) {
|
||||
double degrees = latDDMM.substring(0, 2).toDouble();
|
||||
double minutes = latDDMM.substring(2).toDouble();
|
||||
latitude = degrees + (minutes / 60.0);
|
||||
if (nsIndicator == "S") {
|
||||
latitude = -latitude;
|
||||
}
|
||||
// Skip quotes/spaces
|
||||
while (k < (int)msg.length() && (msg[k]==' ' || msg[k]=='\"')) k++;
|
||||
|
||||
// If quoted value
|
||||
if (k < (int)msg.length() && msg[k-1] == '\"') {
|
||||
int end = msg.indexOf('\"', k);
|
||||
if (end > k) return msg.substring(k, end);
|
||||
}
|
||||
|
||||
double longitude = 0.0;
|
||||
if (lonDDMM.length() > 3) {
|
||||
double degrees = lonDDMM.substring(0, 3).toDouble();
|
||||
double minutes = lonDDMM.substring(3).toDouble();
|
||||
longitude = degrees + (minutes / 60.0);
|
||||
if (ewIndicator == "W") {
|
||||
longitude = -longitude;
|
||||
}
|
||||
}
|
||||
// Numeric value
|
||||
int end = k;
|
||||
while (end < (int)msg.length() && (isDigit(msg[end]) || msg[end]=='.' || msg[end]=='-' )) end++;
|
||||
return msg.substring(k, end);
|
||||
};
|
||||
|
||||
Serial.print("Latitude: ");
|
||||
Serial.println(latitude, 6);
|
||||
Serial.print("Longitude: ");
|
||||
Serial.println(longitude, 6);
|
||||
String deviceId = extract("device_id");
|
||||
String speed = extract("speed");
|
||||
|
||||
} else {
|
||||
Serial.println("Failed to get GPS info from module.");
|
||||
}
|
||||
}
|
||||
Serial.println("Parsed device_id=" + deviceId + " speed=" + speed);
|
||||
|
||||
// --- All other helper functions remain the same ---
|
||||
|
||||
void powerOnModule() {
|
||||
Serial.println("Executing SIM7080G power-on sequence...");
|
||||
pinMode(PWRKEY_PIN, OUTPUT);
|
||||
digitalWrite(PWRKEY_PIN, HIGH);
|
||||
delay(100);
|
||||
digitalWrite(PWRKEY_PIN, LOW);
|
||||
delay(2000);
|
||||
digitalWrite(PWRKEY_PIN, HIGH);
|
||||
}
|
||||
|
||||
void powerOffModule() {
|
||||
Serial.println("Executing SIM7080G power-off sequence...");
|
||||
pinMode(PWRKEY_PIN, OUTPUT);
|
||||
digitalWrite(PWRKEY_PIN, HIGH);
|
||||
delay(100);
|
||||
digitalWrite(PWRKEY_PIN, LOW);
|
||||
delay(2000);
|
||||
digitalWrite(PWRKEY_PIN, HIGH);
|
||||
}
|
||||
|
||||
String sendATCommand(String command, unsigned long timeoutMs) {
|
||||
Serial.print("Sending: ");
|
||||
Serial.println(command);
|
||||
while (Serial2.available()) {
|
||||
Serial2.read();
|
||||
}
|
||||
Serial2.println(command);
|
||||
String response = "";
|
||||
unsigned long startTime = millis();
|
||||
while (millis() - startTime < timeoutMs) {
|
||||
while (Serial2.available()) {
|
||||
char c = (char)Serial2.read();
|
||||
response += c;
|
||||
startTime = millis();
|
||||
if (response.endsWith("OK\r\n") || response.endsWith("ERROR\r\n") ||
|
||||
response.endsWith("FAIL\r\n")) {
|
||||
response.trim();
|
||||
return response;
|
||||
// Example action: if speed present
|
||||
if (speed.length()) {
|
||||
Serial.println("Action: set speed to " + speed);
|
||||
// TODO: apply speed to your app logic
|
||||
}
|
||||
}
|
||||
}
|
||||
response.trim();
|
||||
return response;
|
||||
}
|
||||
|
||||
String getIMEI() {
|
||||
String response = sendATCommand("AT+CGSN", 5000);
|
||||
int currentPos = 0;
|
||||
while (currentPos < response.length()) {
|
||||
int nextNewline = response.indexOf('\n', currentPos);
|
||||
String line = (nextNewline == -1)
|
||||
? response.substring(currentPos)
|
||||
: response.substring(currentPos, nextNewline);
|
||||
currentPos = (nextNewline == -1) ? response.length() : nextNewline + 1;
|
||||
line.trim();
|
||||
if (line.length() == 15) {
|
||||
bool allDigits = true;
|
||||
for (int i = 0; i < line.length(); i++) {
|
||||
if (!isDigit(line.charAt(i))) {
|
||||
allDigits = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (allDigits) {
|
||||
return line;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// This function is no longer called in setup, but is kept for future use
|
||||
bool activatePDPContextAndBearer() {
|
||||
Serial.println("Defining PDP Context...");
|
||||
String response = sendATCommand(
|
||||
"AT+CGDCONT=1,\"IP\",\"" + String(HOLOGRAM_APN) + "\"", 5000);
|
||||
if (response.indexOf("OK") == -1)
|
||||
return false;
|
||||
|
||||
Serial.println("Activating PDP Context...");
|
||||
response = sendATCommand("AT+CGACT=1,1", 10000);
|
||||
if (response.indexOf("OK") == -1)
|
||||
return false;
|
||||
|
||||
Serial.println("Waiting 5 seconds for IP assignment...");
|
||||
delay(5000);
|
||||
|
||||
Serial.println("Checking IP address...");
|
||||
response = sendATCommand("AT+CGPADDR=1", 5000);
|
||||
if (response.indexOf("+CGPADDR: 1,") == -1)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
260
CAR_GPS_TRACKER_2025-08-30_backup.ino
Normal file
260
CAR_GPS_TRACKER_2025-08-30_backup.ino
Normal file
@ -0,0 +1,260 @@
|
||||
#include <HardwareSerial.h>
|
||||
|
||||
// Pins
|
||||
#define LTE_RX_PIN 16 // ESP32 RX <- SIM7080 TX
|
||||
#define LTE_TX_PIN 17 // ESP32 TX -> SIM7080 RX
|
||||
#define PWRKEY_PIN 4 // SIM7080 PWRKEY
|
||||
|
||||
// Network and endpoint
|
||||
const char* APN = "hologram";
|
||||
const char* BASE_URL = "http://laravel-server.lab.audasmedia.com.au";
|
||||
const char* PATH = "/api/gps";
|
||||
|
||||
// -------- Utilities --------
|
||||
String sendAT(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(); // extend timeout on input
|
||||
}
|
||||
}
|
||||
resp.trim();
|
||||
Serial.println("\n[Resp]\n" + resp);
|
||||
return resp;
|
||||
}
|
||||
|
||||
void powerOnSIM7080() {
|
||||
pinMode(PWRKEY_PIN, OUTPUT);
|
||||
digitalWrite(PWRKEY_PIN, LOW);
|
||||
delay(2000);
|
||||
digitalWrite(PWRKEY_PIN, HIGH);
|
||||
delay(1000);
|
||||
digitalWrite(PWRKEY_PIN, LOW);
|
||||
delay(30000); // full boot
|
||||
}
|
||||
|
||||
bool waitLTE(uint32_t ms = 120000) {
|
||||
uint32_t t0 = millis();
|
||||
while (millis() - t0 < ms) {
|
||||
String r = sendAT("AT+CEREG?", 4000);
|
||||
if (r.indexOf("+CEREG: 0,1") != -1 || r.indexOf("+CEREG: 0,5") != -1) return true;
|
||||
delay(3000);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Simple reusable HTTP POST via SH client
|
||||
bool httpPostSH(const String& baseUrl, const String& path, const String& jsonBody) {
|
||||
// Configure HTTP client
|
||||
sendAT("AT+SHCONF=\"URL\",\"" + baseUrl + "\"", 5000);
|
||||
sendAT("AT+SHCONF=\"BODYLEN\",1024", 2000);
|
||||
sendAT("AT+SHCONF=\"HEADERLEN\",350", 2000);
|
||||
|
||||
// Connect (no micromanagement; rely on OK)
|
||||
String r = sendAT("AT+SHCONN", 15000);
|
||||
if (r.indexOf("OK") == -1) {
|
||||
Serial.println("SHCONN failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Clear and set headers
|
||||
sendAT("AT+SHCHEAD", 2000);
|
||||
sendAT("AT+SHAHEAD=\"Content-Type\",\"application/json\"", 2000);
|
||||
|
||||
// Send body
|
||||
String cmd = "AT+SHBOD=" + String(jsonBody.length()) + ",10000";
|
||||
sendAT(cmd, 2000);
|
||||
Serial2.print(jsonBody);
|
||||
delay(200);
|
||||
|
||||
// POST (3 = POST)
|
||||
r = sendAT("AT+SHREQ=\"" + path + "\",3", 20000);
|
||||
// Expect URC like: +SHREQ: "POST",<HTTP_CODE>,<len>
|
||||
// Print status line if present
|
||||
int p = r.indexOf("+SHREQ:");
|
||||
if (p != -1) {
|
||||
Serial.println("Status/URC: " + r.substring(p));
|
||||
}
|
||||
|
||||
// If there is a body, try to read (non-fatal if none)
|
||||
sendAT("AT+SHREAD=0,2048", 4000);
|
||||
|
||||
// Disconnect
|
||||
sendAT("AT+SHDISC", 3000);
|
||||
return true;
|
||||
}
|
||||
|
||||
// -------- Arduino setup/loop --------
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial2.begin(9600, SERIAL_8N1, LTE_RX_PIN, LTE_TX_PIN);
|
||||
Serial.println("--- SIM7080: Simple Connect + HTTP POST ---");
|
||||
|
||||
powerOnSIM7080();
|
||||
|
||||
// Basic checks
|
||||
sendAT("AT", 2000);
|
||||
sendAT("AT+CMEE=1", 2000);
|
||||
sendAT("AT+CPIN?", 2000);
|
||||
sendAT("AT+CSQ", 2000);
|
||||
|
||||
// Force Cat‑M1 + APN attach
|
||||
sendAT("AT+CNMP=38", 5000);
|
||||
sendAT("AT+CMNB=1", 5000);
|
||||
sendAT("AT+CGDCONT=1,\"IP\",\"" + String(APN) + "\"", 5000);
|
||||
sendAT("AT+CGATT=1", 10000);
|
||||
|
||||
// Wait for LTE registration
|
||||
if (!waitLTE()) {
|
||||
Serial.println("No LTE registration; stopping.");
|
||||
while (true) delay(1000);
|
||||
}
|
||||
|
||||
// Activate SIM7080 APP PDP (required for HTTP)
|
||||
sendAT("AT+CNCFG=0,1,\"" + String(APN) + "\"", 5000);
|
||||
sendAT("AT+CNACT=0,1", 45000);
|
||||
sendAT("AT+CNACT?", 3000);
|
||||
|
||||
smsSetup();
|
||||
|
||||
// Optional DNS
|
||||
sendAT("AT+CDNSCFG=\"8.8.8.8\",\"1.1.1.1\"", 3000);
|
||||
|
||||
// Build your JSON (adjust as needed)
|
||||
String json = "{\"device_id\":\"sim7080g-01\",\"lat\":-33.865143,"
|
||||
"\"lng\":151.2099,\"speed\":12.5,\"altitude\":30.2}";
|
||||
|
||||
// Do the POST
|
||||
bool ok = httpPostSH(BASE_URL, PATH, json);
|
||||
Serial.println(ok ? "HTTP POST sent (see status above)" : "HTTP POST failed");
|
||||
|
||||
Serial.println("--- Done ---");
|
||||
}
|
||||
|
||||
void loop() {
|
||||
// Non-blocking read to capture unsolicited lines like +CMTI
|
||||
static String urc;
|
||||
while (Serial2.available()) {
|
||||
char c = (char)Serial2.read();
|
||||
urc += c;
|
||||
if (c == '\n') {
|
||||
urc.trim();
|
||||
if (urc.startsWith("+CMTI:")) {
|
||||
// +CMTI: "SM",<idx>
|
||||
int comma = urc.lastIndexOf(',');
|
||||
if (comma != -1) {
|
||||
int idx = urc.substring(comma + 1).toInt();
|
||||
String body = smsReadAndDelete(idx);
|
||||
Serial.println("SMS (URC): " + body);
|
||||
handleSmsCommand(body);
|
||||
}
|
||||
}
|
||||
urc = "";
|
||||
}
|
||||
}
|
||||
|
||||
// Optional periodic poll in case URC was missed
|
||||
static uint32_t lastPoll = 0;
|
||||
if (millis() - lastPoll > 60000) { // every 60 s
|
||||
smsPollUnread();
|
||||
lastPoll = millis();
|
||||
}
|
||||
|
||||
// Your normal work here...
|
||||
}
|
||||
|
||||
// Call once after PDP is active (or right after registration)
|
||||
void smsSetup() {
|
||||
sendAT("AT+CMGF=1", 2000); // text mode
|
||||
sendAT("AT+CPMS=\"SM\",\"SM\",\"SM\"", 2000); // SIM storage
|
||||
sendAT("AT+CNMI=2,1,0,0,0", 2000); // new SMS URC: +CMTI: "SM",<idx>
|
||||
// Optional: show extended text params
|
||||
// sendAT("AT+CSDH=1", 2000);
|
||||
}
|
||||
|
||||
// Read & delete one SMS by index; returns the body
|
||||
String smsReadAndDelete(int idx) {
|
||||
String r = sendAT("AT+CMGR=" + String(idx), 4000);
|
||||
// Extract last non-empty line before OK
|
||||
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();
|
||||
|
||||
sendAT("AT+CMGD=" + String(idx), 2000);
|
||||
return s;
|
||||
}
|
||||
|
||||
// Optional: poll unread SMS (if you want to scan periodically)
|
||||
void smsPollUnread() {
|
||||
String list = sendAT("AT+CMGL=\"REC UNREAD\"", 5000);
|
||||
// list may contain many entries like:
|
||||
// +CMGL: <idx>,"REC UNREAD",...
|
||||
int pos = 0;
|
||||
while (true) {
|
||||
int p = list.indexOf("+CMGL:", pos);
|
||||
if (p == -1) break;
|
||||
int comma = list.indexOf(',', p);
|
||||
if (comma == -1) break;
|
||||
// Extract index after "+CMGL: "
|
||||
int idxStart = list.indexOf(' ', p);
|
||||
if (idxStart == -1 || idxStart > comma) break;
|
||||
int idx = list.substring(idxStart + 1, comma).toInt();
|
||||
String body = smsReadAndDelete(idx);
|
||||
Serial.println("SMS (polled): " + body);
|
||||
handleSmsCommand(body);
|
||||
pos = comma + 1;
|
||||
}
|
||||
}
|
||||
|
||||
void handleSmsCommand(String msg) {
|
||||
Serial.println("Command received via SMS: " + msg);
|
||||
|
||||
msg.trim();
|
||||
if (msg.isEmpty()) {
|
||||
Serial.println("Empty body");
|
||||
return;
|
||||
}
|
||||
|
||||
// Very simple JSON extraction: look for device_id and speed keys
|
||||
auto extract = [&](const String& key)->String {
|
||||
String pat = String("\"") + key + "\":";
|
||||
int k = msg.indexOf(pat);
|
||||
if (k < 0) return "";
|
||||
k += pat.length();
|
||||
|
||||
// Skip quotes/spaces
|
||||
while (k < (int)msg.length() && (msg[k]==' ' || msg[k]=='\"')) k++;
|
||||
|
||||
// If quoted value
|
||||
if (k < (int)msg.length() && msg[k-1] == '\"') {
|
||||
int end = msg.indexOf('\"', k);
|
||||
if (end > k) return msg.substring(k, end);
|
||||
}
|
||||
|
||||
// Numeric value
|
||||
int end = k;
|
||||
while (end < (int)msg.length() && (isDigit(msg[end]) || msg[end]=='.' || msg[end]=='-' )) end++;
|
||||
return msg.substring(k, end);
|
||||
};
|
||||
|
||||
String deviceId = extract("device_id");
|
||||
String speed = extract("speed");
|
||||
|
||||
Serial.println("Parsed device_id=" + deviceId + " speed=" + speed);
|
||||
|
||||
// Example action: if speed present
|
||||
if (speed.length()) {
|
||||
Serial.println("Action: set speed to " + speed);
|
||||
// TODO: apply speed to your app logic
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user