Larger Storage of file on quectel ec200u-cn In http response

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.