ESP32 + SIM7600 cellular firmware for the in-car agent device: WiFi/AP provisioning with a web admin page, GPRS/LTE connectivity, and IMEI-based identification. The hardcoded default WiFi password has been replaced with a YOUR-WIFI-PASSWORD placeholder so no real credential enters git history. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
408 lines
14 KiB
C++
408 lines
14 KiB
C++
/*
|
|
FILE: ATdebug_WiFi_AP_WebAdmin.ino
|
|
AUTHOR: Kaibin (Modified by OpenAI)
|
|
PURPOSE: Test functionality with WiFi, AP, and Web Admin Panel
|
|
*/
|
|
|
|
#define TINY_GSM_MODEM_SIM7600
|
|
#define TINY_GSM_RX_BUFFER 1024 // Set RX buffer to 1Kb
|
|
#define SerialAT Serial1
|
|
|
|
// See all AT commands, if wanted
|
|
#define DUMP_AT_COMMANDS
|
|
|
|
// set GSM PIN, if any
|
|
#define GSM_PIN ""
|
|
|
|
#define uS_TO_S_FACTOR 1000000ULL /* Conversion factor for micro seconds to seconds */
|
|
#define TIME_TO_SLEEP 30 /* Time ESP32 will go to sleep (in seconds) */
|
|
|
|
#define UART_BAUD 115200
|
|
|
|
#define MODEM_TX 27
|
|
#define MODEM_RX 26
|
|
#define MODEM_PWRKEY 4
|
|
#define MODEM_DTR 32
|
|
#define MODEM_RI 33
|
|
#define MODEM_FLIGHT 25
|
|
#define MODEM_STATUS 34
|
|
|
|
#define SD_MISO 2
|
|
#define SD_MOSI 15
|
|
#define SD_SCLK 14
|
|
#define SD_CS 13
|
|
|
|
#define LED_PIN 12
|
|
|
|
|
|
// Your GPRS credentials, if any
|
|
const char apn[] = "YOUR-APN"; //SET TO YOUR APN
|
|
const char gprsUser[] = "";
|
|
const char gprsPass[] = "";
|
|
|
|
// Default WiFi credentials
|
|
const char* defaultSSID = "AP_1_IOT"; // Replace with your WiFi SSID
|
|
const char* defaultPassword = "YOUR-WIFI-PASSWORD"; // Replace with your WiFi password
|
|
|
|
// Default Access Point credentials
|
|
const char* defaultAPSSID = "ESP32-AP";
|
|
const char* defaultAPPassword = "12345678";
|
|
|
|
#include <TinyGsmClient.h>
|
|
#include <TinyGPS++.h>
|
|
#include <SPI.h>
|
|
#include <SD.h>
|
|
#include <FS.h>
|
|
#include <FFat.h>
|
|
#include <Ticker.h>
|
|
#include <WiFi.h>
|
|
#include <AsyncTCP.h>
|
|
#include <ESPWebFileManager.h>
|
|
#include <ESPAsyncWebServer.h>
|
|
#include <Preferences.h>
|
|
|
|
|
|
#ifdef DUMP_AT_COMMANDS // if enabled it requires the streamDebugger lib
|
|
#include <StreamDebugger.h>
|
|
StreamDebugger debugger(SerialAT, Serial);
|
|
TinyGsm modem(debugger);
|
|
#else
|
|
TinyGsm modem(SerialAT);
|
|
#endif
|
|
|
|
AsyncWebServer server(80);
|
|
ESPWebFileManager fileManager;
|
|
Preferences preferences;
|
|
TinyGPSPlus gps;
|
|
|
|
int counter, lastIndex, numberOfPieces = 24;
|
|
String pieces[24], input;
|
|
|
|
bool reply = false;
|
|
|
|
void modem_on() {
|
|
/*
|
|
The indicator light of the board can be controlled
|
|
*/
|
|
pinMode(LED_PIN, OUTPUT);
|
|
digitalWrite(LED_PIN, HIGH);
|
|
|
|
/*
|
|
MODEM_PWRKEY IO:4 The power-on signal of the modulator must be given to it,
|
|
otherwise the modulator will not reply when the command is sent
|
|
*/
|
|
pinMode(MODEM_PWRKEY, OUTPUT);
|
|
digitalWrite(MODEM_PWRKEY, HIGH);
|
|
delay(300); //Need delay
|
|
digitalWrite(MODEM_PWRKEY, LOW);
|
|
|
|
/*
|
|
MODEM_FLIGHT IO:25 Modulator flight mode control,
|
|
need to enable modulator, this pin must be set to high
|
|
*/
|
|
pinMode(MODEM_FLIGHT, OUTPUT);
|
|
digitalWrite(MODEM_FLIGHT, HIGH);
|
|
|
|
int i = 40;
|
|
Serial.print(F("\r\n# Startup #\r\n"));
|
|
Serial.print(F("# Sending \"AT\" to Modem. Waiting for Response\r\n# "));
|
|
while (i) {
|
|
SerialAT.println(F("AT"));
|
|
|
|
// Show the User: we are doing something.
|
|
Serial.print(F("."));
|
|
delay(500);
|
|
|
|
// Did the Modem send something?
|
|
if (SerialAT.available()) {
|
|
String r = SerialAT.readString();
|
|
Serial.print("\r\n# Response:\r\n" + r);
|
|
if ( r.indexOf("OK") >= 0 ) {
|
|
reply = true;
|
|
break;
|
|
} else {
|
|
Serial.print(F("\r\n# "));
|
|
}
|
|
}
|
|
|
|
// Did the User try to send something? Maybe he did not receive the first messages yet. Inform the User what is happening
|
|
if (Serial.available() && !reply) {
|
|
Serial.read();
|
|
Serial.print(F("\r\n# Modem is not yet online."));
|
|
Serial.print(F("\r\n# Sending \"AT\" to Modem. Waiting for Response\r\n# "));
|
|
}
|
|
|
|
// On the 5th try: Inform the User what is happening
|
|
if (i == 35) {
|
|
Serial.print(F("\r\n# Modem did not yet answer. Probably Power loss?\r\n"));
|
|
Serial.print(F("# Sending \"AT\" to Modem. Waiting for Response\r\n# "));
|
|
}
|
|
delay(500);
|
|
i--;
|
|
}
|
|
Serial.println(F("#\r\n"));
|
|
}
|
|
|
|
void connectToWiFi() {
|
|
// Read saved WiFi credentials
|
|
preferences.begin("wifi", true);
|
|
String ssid = preferences.getString("ssid", "");
|
|
String password = preferences.getString("password", "");
|
|
preferences.end();
|
|
|
|
if (ssid != "") {
|
|
// Connect to WiFi with saved credentials
|
|
WiFi.begin(ssid.c_str(), password.c_str());
|
|
Serial.printf("Connecting to WiFi SSID: %s\n", ssid.c_str());
|
|
} else {
|
|
// Use default credentials if no saved credentials are found
|
|
WiFi.begin(defaultSSID, defaultPassword);
|
|
Serial.println("No saved WiFi credentials found, using default credentials");
|
|
Serial.printf("Connecting to WiFi SSID: %s\n", defaultSSID);
|
|
}
|
|
|
|
// Wait for connection
|
|
while (WiFi.status() != WL_CONNECTED) {
|
|
delay(1000);
|
|
Serial.print(".");
|
|
}
|
|
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
Serial.println("\nConnected to WiFi");
|
|
Serial.print("IP Address: ");
|
|
Serial.println(WiFi.localIP());
|
|
} else {
|
|
Serial.println("\nFailed to connect to WiFi");
|
|
}
|
|
}
|
|
|
|
void setupAccessPoint() {
|
|
// Read saved AP credentials
|
|
preferences.begin("ap", true);
|
|
String apSSID = preferences.getString("apSSID", defaultAPSSID);
|
|
String apPassword = preferences.getString("apPassword", defaultAPPassword);
|
|
preferences.end();
|
|
|
|
Serial.println("Setting up Access Point...");
|
|
|
|
bool result = WiFi.softAP(apSSID.c_str(), apPassword.c_str());
|
|
if (result) {
|
|
Serial.println("Access Point started successfully!");
|
|
Serial.print("AP IP Address: ");
|
|
Serial.println(WiFi.softAPIP());
|
|
} else {
|
|
Serial.println("Failed to start Access Point.");
|
|
}
|
|
}
|
|
|
|
void setupWebServer() {
|
|
// Serve HTML file
|
|
server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){
|
|
// Read saved WiFi credentials
|
|
preferences.begin("wifi", true);
|
|
String ssid = preferences.getString("ssid", defaultSSID);
|
|
String password = preferences.getString("password", defaultPassword);
|
|
preferences.end();
|
|
|
|
// Read saved AP credentials
|
|
preferences.begin("ap", true);
|
|
String apSSID = preferences.getString("apSSID", defaultAPSSID);
|
|
String apPassword = preferences.getString("apPassword", defaultAPPassword);
|
|
preferences.end();
|
|
|
|
// Get IP addresses
|
|
String wifiIP = WiFi.isConnected() ? WiFi.localIP().toString() : "Not connected";
|
|
String apIP = WiFi.softAPIP().toString();
|
|
|
|
// Get MAC addresses
|
|
String wifiMAC = WiFi.macAddress();
|
|
String apMAC = WiFi.softAPmacAddress();
|
|
|
|
// Get GPS data
|
|
String latitude = gps.location.isValid() ? String(gps.location.lat(), 6) : "N/A";
|
|
String longitude = gps.location.isValid() ? String(gps.location.lng(), 6) : "N/A";
|
|
String altitude = gps.altitude.isValid() ? String(gps.altitude.meters()) : "N/A";
|
|
String speed = gps.speed.isValid() ? String(gps.speed.kmph()) : "N/A";
|
|
String satellites = gps.satellites.isValid() ? String(gps.satellites.value()) : "N/A";
|
|
|
|
// Read the HTML file from the filesystem
|
|
File file = FFat.open("/web_admin.html", "r");
|
|
if (!file) {
|
|
request->send(500, "text/plain", "Failed to open web_admin.html");
|
|
return;
|
|
}
|
|
|
|
// Read the file content into a string
|
|
String html = file.readString();
|
|
file.close();
|
|
|
|
// Replace placeholders with actual values
|
|
html.replace("{{WiFiSSID}}", ssid);
|
|
html.replace("{{WiFiPassword}}", password);
|
|
html.replace("{{WiFiIP}}", wifiIP);
|
|
html.replace("{{WiFiMAC}}", wifiMAC);
|
|
html.replace("{{APSSID}}", apSSID);
|
|
html.replace("{{APPassword}}", apPassword);
|
|
html.replace("{{APIP}}", apIP);
|
|
html.replace("{{APMAC}}", apMAC);
|
|
html.replace("{{Latitude}}", latitude);
|
|
html.replace("{{Longitude}}", longitude);
|
|
html.replace("{{Altitude}}", altitude);
|
|
html.replace("{{Speed}}", speed);
|
|
html.replace("{{Satellites}}", satellites);
|
|
|
|
// Send the modified HTML content
|
|
request->send(200, "text/html", html);
|
|
});
|
|
|
|
// Handle WiFi configuration form submission
|
|
server.on("/setWiFi", HTTP_POST, [](AsyncWebServerRequest *request) {
|
|
String ssid = request->getParam("ssid", true)->value();
|
|
String password = request->getParam("password", true)->value();
|
|
|
|
// Save the credentials to a secure location
|
|
preferences.begin("wifi", false);
|
|
preferences.putString("ssid", ssid);
|
|
preferences.putString("password", password);
|
|
preferences.end();
|
|
|
|
Serial.printf("New WiFi SSID: %s, Password: %s\n", ssid.c_str(), password.c_str());
|
|
|
|
// Restart WiFi with new credentials
|
|
WiFi.disconnect();
|
|
WiFi.begin(ssid.c_str(), password.c_str());
|
|
|
|
// Wait for connection
|
|
while (WiFi.status() != WL_CONNECTED) {
|
|
delay(1000);
|
|
Serial.print(".");
|
|
}
|
|
|
|
if (WiFi.status() == WL_CONNECTED) {
|
|
Serial.println("\nConnected to WiFi");
|
|
Serial.print("IP Address: ");
|
|
Serial.println(WiFi.localIP());
|
|
} else {
|
|
Serial.println("\nFailed to connect to WiFi");
|
|
}
|
|
|
|
// Send a response with a popup alert
|
|
String response = "<html><body><script>alert('WiFi credentials updated successfully.'); window.location.href = '/';</script></body></html>";
|
|
request->send(200, "text/html", response);
|
|
});
|
|
|
|
// Handle Access Point configuration form submission
|
|
server.on("/setAP", HTTP_POST, [](AsyncWebServerRequest *request) {
|
|
String apSSID = request->getParam("apSSID", true)->value();
|
|
String apPassword = request->getParam("apPassword", true)->value();
|
|
|
|
// Save the credentials to a secure location
|
|
preferences.begin("ap", false);
|
|
preferences.putString("apSSID", apSSID);
|
|
preferences.putString("apPassword", apPassword);
|
|
preferences.end();
|
|
|
|
Serial.printf("New AP SSID: %s, Password: %s\n", apSSID.c_str(), apPassword.c_str());
|
|
|
|
// Restart Access Point with new credentials
|
|
WiFi.softAPdisconnect(true);
|
|
bool result = WiFi.softAP(apSSID.c_str(), apPassword.c_str());
|
|
if (result) {
|
|
Serial.println("Access Point started successfully!");
|
|
Serial.print("AP IP Address: ");
|
|
Serial.println(WiFi.softAPIP());
|
|
} else {
|
|
Serial.println("Failed to start Access Point.");
|
|
}
|
|
|
|
// Send a response with a popup alert
|
|
String response = "<html><body><script>alert('Access Point credentials updated successfully.'); window.location.href = '/';</script></body></html>";
|
|
request->send(200, "text/html", response);
|
|
});
|
|
|
|
// Start server
|
|
server.begin();
|
|
Serial.println("Web server started.");
|
|
}
|
|
|
|
void fatfs () {
|
|
if (!FFat.begin(true)) { // Format on fail: 'true' forces formatting if mounting fails
|
|
Serial.println("Failed to initialize eMMC storage (FFat). Trying to format...");
|
|
if (!FFat.format()) {
|
|
Serial.println("FFat format failed. Check partition table and storage.");
|
|
return; // Halt setup if FFat fails
|
|
}
|
|
if (!FFat.begin()) {
|
|
Serial.println("Failed to mount FFat after formatting.");
|
|
return;
|
|
}
|
|
}
|
|
Serial.println("FFat initialized successfully.");
|
|
}
|
|
|
|
void setup() {
|
|
Serial.begin(115200); // Set console baud rate
|
|
SerialAT.begin(115200, SERIAL_8N1, MODEM_RX, MODEM_TX);
|
|
delay(100);
|
|
|
|
fatfs ();
|
|
modem_on();
|
|
connectToWiFi();
|
|
setupAccessPoint();
|
|
setupWebServer();
|
|
|
|
if (reply) {
|
|
Serial.println(F("***********************************************************"));
|
|
Serial.println(F(" You can now send AT commands"));
|
|
Serial.println(F(" Enter \"AT\" (without quotes), and you should see \"OK\""));
|
|
Serial.println(F(" If it doesn't work, select \"Both NL & CR\" in Serial Monitor"));
|
|
Serial.println(F(" DISCLAIMER: Entering AT commands without knowing what they do"));
|
|
Serial.println(F(" can have undesired consiquinces..."));
|
|
Serial.println(F("***********************************************************\n"));
|
|
|
|
// Uncomment to read received SMS
|
|
//SerialAT.println("AT+CMGL=\"ALL\"");
|
|
} else {
|
|
Serial.println(F("***********************************************************"));
|
|
Serial.println(F(" Failed to connect to the modem! Check the baud and try again."));
|
|
Serial.println(F("***********************************************************\n"));
|
|
}
|
|
|
|
// Initialize FATFS (Change to other types as needed, Valid types: FS_SD_CARD, FS_SPIFFS, FS_LITTLEFS, FS_FATFS )
|
|
if (!fileManager.initFileSystem(ESPWebFileManager::FS_FATFS, true)) {
|
|
DEBUG_SERIAL.println("Failed to initialize file system");
|
|
}
|
|
|
|
fileManager.setServer(&server);
|
|
server.begin();
|
|
DEBUG_SERIAL.println("Web server started");
|
|
|
|
|
|
}
|
|
|
|
void loop() {
|
|
if (SerialAT.available()) {
|
|
Serial.write(SerialAT.read());
|
|
}
|
|
if (Serial.available()) {
|
|
SerialAT.write(Serial.read());
|
|
}
|
|
// Read data from the GPS module
|
|
while (SerialAT.available() > 0) {
|
|
gps.encode(SerialAT.read());
|
|
}
|
|
// Print GPS data to the serial monitor
|
|
if (gps.location.isUpdated()) {
|
|
Serial.print("Latitude: ");
|
|
Serial.println(gps.location.lat(), 6);
|
|
Serial.print("Longitude: ");
|
|
Serial.println(gps.location.lng(), 6);
|
|
Serial.print("Altitude: ");
|
|
Serial.println(gps.altitude.meters());
|
|
Serial.print("Speed: ");
|
|
Serial.println(gps.speed.kmph());
|
|
Serial.print("Satellites: ");
|
|
Serial.println(gps.satellites.value());
|
|
}
|
|
delay(1000);
|
|
} |