CAR_GPS_REACT/prisma/schema.prisma
2025-09-09 11:30:30 +10:00

293 lines
7.9 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

model Device {
id String @id @default(uuid())
imei String @unique
name String?
telemetry Telemetry[]
@@map("devices")
}
model Telemetry {
id String @id @default(uuid())
deviceId String
device Device @relation(fields: [deviceId], references [id])
recordedAt DateTime @default(now)
lat Float
lng Float
altitude Float?
speed Float?
heading Float?
accuracy Float?
battery Float?
isCarOn Boolean?
raw Json?
@@map("telemetry")
}
model Command {
id Int @id @default(autoincrement())
deviceId String
device Device @relation(fields: [deviceId], references [id])
type String
payload Json
createdAt DateTime @default(now())
@@map("commands")
}iniclude <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 CatM1 + 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
}
}