I have module named “ESP32-S3 EC200U 4G LTE Cat-1 GNSS WiFi Bluetooth IoT Modem - 7Semi” and i have a server with a .bin file i want to download and flash it in es32s3 using cellural network can anybody help me with at commands i’m using arduino. void updatefirmware() {
Serial.println(“— Starting CELLULAR OTA Firmware Update —”);
Serial.print("Target URL: ");
Serial.println(FIRMWARE_URL);
// 1. Temporarily disconnect from MQTT (to free up modem context for HTTP)
Serial.println(“Stopping modem MQTT connection…”);
sendATCommand(“AT+QMTDISC=0”, 5000);
// Disable WiFi SoftAP during OTA to reduce conflicts
Serial.println(“Disabling WiFi SoftAP for OTA…”);
WiFi.softAPdisconnect(true);
delay(1000);
// Variables used throughout the process
bool otaSuccess = false;
int contentLength = 0;
bool stepOK = true; // Flag to manage sequential flow
esp_ota_handle_t ota_handle = 0; // OTA handle for low-level ops
esp_err_t ota_err = ESP_OK; // Track overall OTA error
// 2. Configure HTTP context (assuming PDP context 1 is already active from MQTT setup)
Serial.println(“Configuring HTTP connection…”);
sendATCommand(“AT+QHTTPCFG="contextid",” + String(HTTP_CONTEXT_ID), 1000);
// Set response header format to 0 (no response header) for simplicity
sendATCommand(“AT+QHTTPCFG="responseheader",0”, 1000);
// 3. Set the URL
if (stepOK) {
Serial.print(“Setting URL (length “);
Serial.print(strlen(FIRMWARE_URL));
Serial.println(”)…”);
// Command: AT+QHTTPURL=<url_len>,<timeout>
String urlCommand = "AT+QHTTPURL=" + String(strlen(FIRMWARE_URL)) + ",60"; // 60s timeout
String urlResponse = sendATCommand(urlCommand, 500); // Wait for CONNECT prompt
if (urlResponse.indexOf("CONNECT") != -1) {
SerialAT.println(FIRMWARE_URL); // Send the URL payload
} else {
Serial.println("ERROR: Failed to prepare URL for HTTP. Aborting URL set.");
stepOK = false;
}
// Wait for response after sending URL (Expecting OK or ERROR)
if (stepOK) {
urlResponse = sendATCommand("", 10000);
if (urlResponse.indexOf("OK") == -1) {
Serial.println("ERROR: Modem rejected URL. Aborting URL set.");
stepOK = false;
}
}
}
// 4. Send GET Request and parse size
int httpCode = 0;
if (stepOK) {
Serial.println(“Sending HTTP GET request…”);
// Wait up to 65 seconds for the entire GET operation
String getResponse = sendATCommand(“AT+QHTTPGET=60”, 65000);
// Check for success code and parse it
int getResponseIndex = getResponse.indexOf("+QHTTPGET:");
if (getResponseIndex != -1) {
int colonIndex = getResponse.indexOf(':', getResponseIndex);
if (colonIndex != -1) {
String dataPart = getResponse.substring(colonIndex + 1);
dataPart.trim(); // Remove leading/trailing whitespace (handles the space after :)
// dataPart now = "0,200,329472"
String errStr = getValue(dataPart, ',', 0);
if (errStr == "0") {
String codeStr = getValue(dataPart, ',', 1);
httpCode = codeStr.toInt();
// Extract the content size (index 2, if present)
String sizeStr = getValue(dataPart, ',', 2);
contentLength = sizeStr.toInt();
}
}
}
// Use the parsed code to determine success
if (httpCode != 200 && httpCode != 301) {
Serial.printf("ERROR: HTTP GET failed. Code: %d. Aborting GET.\n", httpCode);
stepOK = false;
contentLength = 0; // Ensure content length is reset if GET failed
} else {
Serial.printf("HTTP GET successful. Code: %d. Firmware size: %d bytes\n", httpCode, contentLength);
}
}
// 5. Check Content Length (extracted in Step 4)
if (stepOK) {
if (contentLength <= 0) {
Serial.println(“ERROR: Content length reported as 0 or could not be extracted from +QHTTPGET response. Aborting content read.”);
stepOK = false;
} else {
Serial.printf(“Content length confirmed: %d bytes\n”, contentLength);
}
}
// 6. Validate and start OTA process
if (stepOK) {
const esp_partition_t *next_part = esp_ota_get_next_update_partition(NULL);
if (!next_part || next_part->size < contentLength) {
Serial.printf(“ERROR: OTA partition invalid (size: %d, needed: %d)\n”, next_part ? next_part->size : 0, contentLength);
stepOK = false;
return;
}
Serial.printf(“OTA to partition ‘%s’ at 0x%08x (size: %d bytes)\n”, next_part->label, next_part->address, next_part->size);
esp_err_t init_err = esp_ota_begin(next_part, contentLength, &ota_handle);
if (init_err != ESP_OK) {
Serial.printf("ERROR: esp_ota_begin failed: %s (code %d)\n", esp_err_to_name(init_err), init_err);
stepOK = false;
ota_err = init_err;
return;
}
Serial.println("OTA update partition ready. Reading and writing firmware...");
}
// 7. Request to read the entire file and stream it
if (stepOK) {
Serial.println(“Requesting binary stream read…”);
String readCommand = “AT+QHTTPREAD=60”; // wait_time=60s (max interval between packets)
SerialAT.println(readCommand);
Serial.print("\n> ");
Serial.println(readCommand);
// Read initial response until CONNECT (up to 30 seconds)
String initialResponse;
unsigned long startTime = millis();
while (millis() - startTime < 30000) {
while (SerialAT.available()) {
char c = (char)SerialAT.read();
initialResponse += c;
}
if (initialResponse.indexOf("CONNECT") != -1) {
break;
}
}
if (initialResponse.indexOf("CONNECT") == -1) {
Serial.println("ERROR: Did not receive CONNECT prompt for binary read. Aborting stream.");
// Attempt to cancel any pending operation
SerialAT.write(0x1A); // Send Ctrl+Z to break out of data mode
stepOK = false;
ota_err = ESP_ERR_TIMEOUT;
} else {
// Stream the binary data
Serial.println("Streaming binary data from modem...");
size_t written = 0;
size_t totalRead = 0;
const size_t chunkSize = 4096; // Sector-aligned for ESP32-S3 flash
uint8_t buffer[chunkSize] __attribute__((aligned(4))); // 4-byte aligned
// Use a longer timeout for the entire stream process (increased to 5 minutes)
unsigned long streamTimeout = 300000; // 300 seconds
unsigned long streamStartTime = millis();
unsigned long lastDataTime = millis(); // Track last data receipt for per-packet timeout
// Loop to read data directly from SerialAT and write to OTA.
while (totalRead < contentLength) {
// Check for overall stream timeout
if (millis() - streamStartTime > streamTimeout) {
Serial.println("ERROR: Streaming timeout reached.");
ota_err = ESP_ERR_TIMEOUT;
break;
}
// Check for available data
if (SerialAT.available()) {
size_t bytesToRead = SerialAT.available();
if (totalRead + bytesToRead > contentLength) {
// Ensure we don't read past the declared content length
bytesToRead = contentLength - totalRead;
}
if (bytesToRead > chunkSize) {
bytesToRead = chunkSize; // Cap at chunk size
}
// Read bytes directly into the buffer
size_t bytesRead = SerialAT.readBytes((uint8_t*)buffer, bytesToRead);
if (bytesRead > 0) {
// Write to OTA using low-level API and check for errors
esp_err_t write_err = esp_ota_write(ota_handle, (const void*)buffer, bytesRead);
if (write_err != ESP_OK) {
Serial.printf("OTA write error: %s (code %d)\n", esp_err_to_name(write_err), write_err);
ota_err = write_err;
stepOK = false;
break;
}
written += bytesRead;
totalRead += bytesRead;
// Update timers
streamStartTime = millis();
lastDataTime = millis();
// Optional: print progress every 10% and log bytes per chunk for debugging
int progress = (totalRead * 100) / contentLength;
if (progress != ((totalRead - bytesRead) * 100) / contentLength) {
Serial.printf("Progress: %d%% (chunk: %d bytes)\n", progress, bytesRead);
} else if (totalRead % 10000 == 0) { // Log every ~10KB for ongoing chunks
Serial.printf("Streaming: %d/%d bytes read\n", totalRead, contentLength);
}
}
} else {
// No data available; check if we've waited too long since last packet (align with QHTTPREAD wait_time)
if (millis() - lastDataTime > 60000) { // 60s no-data timeout
Serial.println("ERROR: No data received for 60 seconds. Aborting stream.");
ota_err = ESP_ERR_TIMEOUT;
stepOK = false;
break;
}
vTaskDelay(pdMS_TO_TICKS(5)); // Yield to flash task (better than delay for S3)
}
}
Serial.printf("Finished reading stream. Total bytes expected: %d, Total bytes read: %d, Total bytes written to OTA: %d\n", contentLength, totalRead, written);
// 8. Finalize Update and check modem status response
if (stepOK && written == contentLength) {
esp_err_t end_err = esp_ota_end(ota_handle);
ota_err = end_err; // Update overall error
if (end_err == ESP_OK) {
const esp_partition_t *next_part = esp_ota_get_next_update_partition(NULL);
esp_err_t set_err = esp_ota_set_boot_partition(next_part);
if (set_err == ESP_OK) {
Serial.println("Update successful. Finalizing and restarting...");
otaSuccess = true;
// After a successful OTA, restart the ESP32 to boot the new firmware
Serial.println("Restarting ESP32...");
delay(100);
ESP.restart(); // Add an explicit restart command
} else {
Serial.printf("ERROR: esp_ota_set_boot_partition failed: %s (code %d)\n", esp_err_to_name(set_err), set_err);
ota_err = set_err;
}
} else {
Serial.printf("ERROR: esp_ota_end failed: %s (code %d)\n", esp_err_to_name(end_err), end_err);
}
} else {
esp_ota_end(ota_handle); // Clean up even on failure
Serial.printf("ERROR: Update failed. Wrote %d of %d bytes. OTA Error Code: %d (%s)\n", written, contentLength, ota_err, esp_err_to_name(ota_err));
}
}
}
// Explicitly close the HTTP read session
if (stepOK || !otaSuccess) {
Serial.println(“Closing HTTP read session…”);
sendATCommand(“AT+QHTTPREAD=0”, 5000);
}
// 9. Cleanup: This block runs regardless of success or failure.
if (!otaSuccess) {
Serial.println(“Update process finished. Resuming operation…”);
}
// Clear HTTP context
sendATCommand(“AT+QHTTPURL=0”, 500); // Close URL session
// Re-establish WiFi SoftAP
WiFi.softAP(ssid, password);
Serial.println(“WiFi SoftAP restarted.”);
// Re-establish MQTT connection (via modem)
connectMQTT();
}
Blockquote