I have connected my esp32 with quectel ec200u-cn module.
I am updating my firmware using module.
When I am generating HTTP request on url I am getting file around 1 MB.
When I am trying to store file in module it’s showing CME error for memory allocation failed.
How can I solve that issue.
File size is 1 mb and I want to store that 1 mb in UFS. (Total 2 MB is required)
I tested it before, if I write byte by byte it is ok. Don’t write the file to buffer all at once.
Can I have reference for that ? Because I have tried to read HTTP manual and File manual both but unable to find solution for that.
Even tried HTTPREADFILE but unable to perform that also.
If I can have a specific manual to do so it would be really helpful to me.
You can share manual or tutorial if you have any, so I can learn.
Here is an example code to upload the file to the Quectel module.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <termios.h>
#include <sys/select.h>
#include <time.h>
#include <sys/types.h>
#include <sys/stat.h>
// Define constants for read return values
#define READ_OK (-1)
#define READ_ERROR (-2)
#define READ_NOTHING (-3)
#define READ_EXCEPT (-4)
#define READ_CONNECT (-5)
#define READ_END (-6)
#define QFUPL_TX_SIZE (4 * 1024)
#define VTIME_VALUE 20
/**
* Opens a serial port with specified settings.
* @param port The path to the serial port device.
* @return The file descriptor of the opened port, or -1 on error.
*/
int open_serial_port(const char *port) {
struct termios newtio;
int fd = open(port, O_RDWR | O_NDELAY);
if (fd == -1) {
return -1;
}
// Set the file descriptor to blocking mode
fcntl(fd, F_SETFL, 0);
tcgetattr(fd, &newtio);
// Configure baud rate, character size, stop bits, and parity
cfsetispeed(&newtio, B115200);
cfsetospeed(&newtio, B115200);
newtio.c_cflag &= ~CSIZE;
newtio.c_cflag |= CS8;
newtio.c_cflag &= ~CSTOPB;
newtio.c_cflag |= CLOCAL | CREAD;
newtio.c_cflag &= ~(PARENB | PARODD);
// Configure input flags
newtio.c_iflag &= ~(INPCK | BRKINT | PARMRK | ISTRIP | INLCR | IGNCR | ICRNL);
newtio.c_iflag |= IGNBRK;
newtio.c_iflag &= ~(IXON | IXOFF | IXANY);
// Disable canonical mode and output processing
newtio.c_lflag = 0;
newtio.c_oflag = 0;
// Set read timeout
newtio.c_cc[VMIN] = 0;
newtio.c_cc[VTIME] = VTIME_VALUE;
// Apply the configuration
tcsetattr(fd, TCSANOW, &newtio);
return fd;
}
/**
* Extracts the error code from a response buffer.
* @param buffer The response buffer to parse.
* @return The extracted error code, or -1 if not found.
*/
int get_error_code(char *buffer) {
char *ptr = strstr(buffer, "+CME ERROR");
if (ptr) {
ptr += strlen("+CME ERROR:");
return atoi(ptr);
}
return -1;
}
/**
* Sends a command to the serial port.
* @param fd The file descriptor of the serial port.
* @param command The command to send.
* @return 0 on success, or -1 on error.
*/
int send_command(int fd, char *command) {
char cmd_buffer[255];
strcpy(cmd_buffer, command);
strcat(cmd_buffer, "\r");
return write(fd, cmd_buffer, strlen(cmd_buffer)) == strlen(cmd_buffer) ? 0 : -1;
}
/**
* Reads a line from the serial port.
* @param fd The file descriptor of the serial port.
* @param buffer The buffer to store the read line.
* @param buf_len The length of the buffer.
* @return The length of the read line, or a negative error code.
*/
int read_line(int fd, char *buffer, int buf_len) {
int count, res, read_sum = 0;
fd_set read_fds, except_fds;
struct timeval timeout;
FD_ZERO(&read_fds);
FD_ZERO(&except_fds);
FD_SET(fd, &read_fds);
FD_SET(fd, &except_fds);
timeout.tv_sec = 1;
timeout.tv_usec = 0;
res = select(fd + 1, &read_fds, NULL, &except_fds, &timeout);
if (res == 0) {
return READ_NOTHING;
} else if (res < 0 || FD_ISSET(fd, &except_fds)) {
return READ_EXCEPT;
}
while ((count = read(fd, &buffer[read_sum], buf_len - read_sum)) > 0) {
read_sum += count;
if (buffer[read_sum - 1] == '\n' || buffer[read_sum - 1] == '\r') {
break;
}
}
if (count < 0) {
return READ_EXCEPT;
}
buffer[read_sum] = '\0';
if (strstr(buffer, "\nOK")) {
return READ_OK;
} else if (strstr(buffer, "CONNECT")) {
return READ_CONNECT;
} else if (strstr(buffer, "\nERROR") || strstr(buffer, "\n+CME ERROR")) {
return READ_ERROR;
}
return strlen(buffer) > 0 ? strlen(buffer) : READ_NOTHING;
}
/**
* Reads a reply from the serial port until a valid response is received.
* @param fd The file descriptor of the serial port.
* @param buffer The buffer to store the reply.
* @param buf_len The length of the buffer.
* @return The result code of the last read line, or a negative error code.
*/
int read_reply(int fd, char *buffer, int buf_len) {
int res = -1, count = 0;
do {
res = read_line(fd, buffer, buf_len);
if (res != READ_NOTHING && res != READ_EXCEPT) {
for (int i = 0; i < strlen(buffer); i++) {
if (buffer[i] == '\r' || buffer[i] == '\n') {
buffer[i] = ' ';
}
}
printf("< %d - [%d] - '%s'\n", count, res, buffer);
if (res == READ_ERROR) {
int error_code = get_error_code(buffer);
printf("< Error code: %d\n", error_code);
}
}
count++;
} while (res != READ_OK && res != READ_ERROR && res != READ_EXCEPT && res != READ_CONNECT);
return res;
}
/**
* Closes a serial port.
* @param fd The file descriptor of the serial port to close.
*/
void close_port(int fd) {
close(fd);
}
/**
* Prints help information for the program.
* @param argv The argument vector.
*/
void print_help_info(char *argv[]) {
printf(" function: send DFOTA package to UFS path of module via AT port\n");
printf(" usage: %s -f DFOTA_package -p AT_port\n", argv[0]);
printf(" DFOTA_package: regular file under any path\n");
printf(" AT_port: device file under /dev, /dev/ttyUSB2 is useful in most cases\n");
}
/**
* Gets the file type of a given file path.
* @param file_path The path to the file.
* @return The file type, or S_IFMT on error.
*/
mode_t get_file_type(const char *file_path) {
struct stat file_stat;
if (stat(file_path, &file_stat) == 0) {
return file_stat.st_mode;
}
return S_IFMT;
}
/**
* Extracts the file name from a file path.
* @param file_path The file path to extract the name from.
* @return A pointer to the file name, or the original path if no directory separator is found.
*/
char *get_file_name_from_path(char *file_path) {
char *p = strrchr(file_path, '/');
return p ? p + 1 : file_path;
}
/**
* Sends data to the serial port in chunks.
* @param port_fd The file descriptor of the serial port.
* @param buff The data buffer to send.
* @param send_num The number of bytes to send.
*/
void send_data_to_port(int port_fd, const char *buff, int send_num) {
int count, written_sum = 0;
while (1) {
count = write(port_fd, &buff[written_sum], send_num - written_sum);
if (count > 0) {
written_sum += count;
if (written_sum == send_num) {
return;
}
} else {
printf("write package failure, error: %s\n", strerror(errno));
}
printf("try to resend package in 1 second\n");
sleep(1);
}
}
int main(int argc, char *argv[]) {
int opt, gFd;
char fPath[256], sPort[256];
char *fName;
char sCommand[128], sBuffer[256];
char c[QFUPL_TX_SIZE];
long long int progress, size, iRe, nFileSize = 0;
// Initialize buffers
memset(fPath, 0, sizeof(fPath));
memset(sPort, 0, sizeof(sPort));
memset(sCommand, 0, sizeof(sCommand));
memset(sBuffer, 0, sizeof(sBuffer));
// Parse command line arguments
while ((opt = getopt(argc, argv, "p:f:h")) != -1) {
switch (opt) {
case 'f':
if (access(optarg, F_OK) != 0) {
printf("ERROR: File not found\n");
return 1;
}
strncpy(fPath, optarg, sizeof(fPath) - 1);
break;
case 'p':
if (access(optarg, F_OK) != 0) {
printf("ERROR: Port not found\n");
return 1;
}
strncpy(sPort, optarg, sizeof(sPort) - 1);
break;
case 'h':
print_help_info(argv);
exit(0);
}
}
// Check if required parameters are provided
if (fPath[0] == '\0') {
printf("ERROR: Missing file parameter\n");
return 1;
}
if (sPort[0] == '\0') {
printf("ERROR: Missing port parameter\n");
return 1;
}
if (!S_ISREG(get_file_type(fPath))) {
printf("ERROR: DFOTA package is not a regular file\n");
return 1;
}
if (!S_ISCHR(get_file_type(sPort))) {
printf("ERROR: AT port is not a character device file\n");
return 1;
}
// Open the file and serial port
FILE *pFile = fopen(fPath, "rb");
if (!pFile) {
printf("ERROR: Open file failed\n");
return 1;
}
fseek(pFile, 0, SEEK_END);
size = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
gFd = open_serial_port(sPort);
if (gFd == -1) {
printf("ERROR: Open port failed\n");
return 1;
}
// Prepare and send the delete file command
fName = get_file_name_from_path(fPath);
snprintf(sCommand, sizeof(sCommand), "AT+QFDEL=\"UFS:%s\"", fName);
printf("fName = %s\n", fName);
send_command(gFd, sCommand);
memset(sCommand, 0, sizeof(sCommand));
usleep(300000);
read_reply(gFd, sBuffer, sizeof(sBuffer));
// Prepare and send the upload file command
printf("fName = %s, size = %d\n", fName, size);
snprintf(sCommand, sizeof(sCommand), "AT+QFUPL=\"UFS:%s\",%lld", fName, size);
send_command(gFd, sCommand);
if (read_reply(gFd, sBuffer, sizeof(sBuffer)) != READ_CONNECT) {
printf("the response of QFUPL command is not \"CONNECT\"\n");
return 1;
}
// Send the file data in chunks
while (!feof(pFile)) {
if (nFileSize >= size) {
printf("nFileSize : %d, size:%d \n", nFileSize, size);
break;
}
iRe = fread(&c, 1, sizeof(c), pFile);
nFileSize += iRe;
send_data_to_port(gFd, c, iRe);
usleep(3000);
progress = nFileSize * 100 / size;
printf(progress == 100 ? "progress : %lld%% finished\n" : "progress : %lld%% finished\r", progress);
usleep(300000); // Adjusted sleep time for better performance
}
read_reply(gFd, sBuffer, sizeof(sBuffer));
// Clean up
close_port(gFd);
fclose(pFile);
return 0;
}
1 Like
Thanks for response.