Ошибка StoreProhibited при попытке чтения файла в PROGMEM

Я пытаюсь прочитать файл с micro SD на плате ESP32-cam и загрузить его на FTP-сервер, но когда я пытаюсь прочитать данные из открытого файла в unsigned char, это дает мне следующую ошибку:

Guru Meditation Error: Core  1 panic'ed (StoreProhibited). Exception was unhandled.
Core 1 register dump:
PC      : 0x4008c674  PS      : 0x00060030  A0      : 0x800ed1c7  A1      : 0x3ffb1e20  
A2      : 0x00000000  A3      : 0x3ffd7c78  A4      : 0x00000080  A5      : 0x00000000  
A6      : 0x474e5089  A7      : 0x0a1a0a0d  A8      : 0x00000000  A9      : 0x3ffb1df0  
A10     : 0x00000080  A11     : 0x3ffb66e4  A12     : 0x3ffd7c78  A13     : 0x00000080  
A14     : 0x00002494  A15     : 0xff000000  SAR     : 0x00000018  EXCCAUSE: 0x0000001d  
EXCVADDR: 0x00000000  LBEG    : 0x4008c670  LEND    : 0x4008c68c  LCOUNT  : 0x00000007  

ELF file SHA256: 0000000000000000

Backtrace: 0x4008c674:0x3ffb1e20 0x400ed1c4:0x3ffb1e30 0x400ed245:0x3ffb1e60 0x400d06e7:0x3ffb1e80 0x400d084f:0x3ffb1f20 0x400d2b5a:0x3ffb1fb0 0x4008efde:0x3ffb1fd0

Rebooting...

Код выглядит следующим образом:

// Wi-Fi и FTP - клиент Lib
#include <WiFi.h>
#include <WiFiClient.h> 
#include "ESP32_FTPClient.h"

// MicroSD
#include "driver/sdmmc_host.h"
#include "driver/sdmmc_defs.h"
#include "sdmmc_cmd.h"
#include "esp_vfs_fat.h"

// Учетные данные FTP-сервера
char ftp_server[] = "192.168.1.50";
char ftp_user[]   = "username";
char ftp_pass[]   = "password";

// wi-Fi доступ
const char* ssid = "ssid";
const char* password = "psk";

// файл для чтения и загрузки
FILE *fileUpload = NULL;

// ftp client object
ESP32_FTPClient ftp (ftp_server, ftp_user, ftp_pass);

// micro sd initialization
static esp_err_t init_sdcard()
{
  esp_err_t ret = ESP_FAIL;
  sdmmc_host_t host = SDMMC_HOST_DEFAULT();
  sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
  esp_vfs_fat_sdmmc_mount_config_t mount_config = {
    .format_if_mount_failed = false,
    .max_files = 10,
  };
  sdmmc_card_t *card;
  ret = esp_vfs_fat_sdmmc_mount("/sdcard", &host, &slot_config, &mount_config, &card);
}

void FTP_upload( void );
void FTP_upload()
{

  // initialize file name
  char fileName[100];
  sprintf(fileName, "/sdcard/img.png");
  fileUpload = fopen(fileName, "r+");
  
  // notify reading of the file
  Serial.print("Reading file: ");
  Serial.println(fileName);
  if (fileUpload == NULL) 
  {
    Serial.println("Cannot open file");
    return;
  }

  // получить размер файла
  fseek(fileUpload, 0, SEEK_END);
  long int size = ftell(fileUpload);
  fclose(fileUpload);

  // печать размера файла в серийный порт
  Serial.print("File size: ");
  Serial.println(size);
  
  // Чтение данных в массив unsigned char  fileUpload = fopen(fileName, "r+");
  unsigned char * in PROGMEM = (unsigned char *) malloc(size);
  
  // отображение выделенной памяти
  Serial.print("Allocated memory: ");
  Serial.println(sizeof(in));

  // read file to allocated memory
  int bytes_read = fread(in, sizeof(unsigned char), size, fileUpload); // <------- THIS ROW RISES THE ERROR
  
// close file
  fclose(fileUpload);

  // Открыть FTP-соединение
  Serial.println("Starting FTP upload...");
  ftp.OpenConnection();
  
  //Создать файл и записать в него данные изображения;
  ftp.InitFile("Type I");

  // изменить удаленный каталог
  ftp.ChangeWorkDir("/Shared/");

  // имя удаленного файла
  String strNome = "img.png";

  // преобразовать строку в char
  const char *f_name = strNome.c_str();
  
  // создайте новый файл с заданным именем внутри удаленного каталога
  ftp.NewFile( f_name );

  // write data read above from local file to remote file
  ftp.WriteData(in, sizeof(in));

  // close remote file
  ftp.CloseFile();

  // notify completed upload
  Serial.println("File uploaded successfully");
  
  // wait
  delay(100);

}

void setup() {

  // begin serial communication
  Serial.begin(115200);

  // initialize micro sd
  esp_err_t errSD = init_sdcard();
  if (errSD != ESP_OK) {
    Serial.printf("Не удается инициализировать SD. Код ошибки: 0x%x", errSD);
    return;
  }

  // начать подключение Wi-Fi
  WiFi.begin(ssid, password);

  // wait for the connection to be estabilished
  while (WiFi.status() != WL_CONNECTED) {
    delay(500);
    Serial.print(".");
  }

  // уведомить об успешном соединении
  Serial.println("");
  Serial.println("WiFi connected");

  // показать назначенный IP-адрес
  Serial.print("IP: ");
  Serial.println(WiFi.localIP());

  // вызов процедуры загрузки по FTP
  FTP_upload();
  
}

void loop() {
  // пусто
}

Как я могу это решить? Спасибо

, 👍1

Обсуждение

Эта строка для меня не имеет смысла: символ без знака * в программе = (символ без знака *) malloc(размер); Либо это в ПРОГМЕ, либо в куче. Это не может быть и тем, и другим. [Я не думаю, что это ваша проблема, так как PROGMEM не существует в ESP32 ASFAIK], @PMF

Хех, это все, что нам нужно: progmalloc(). Или это " malloc_P ()"?=) Я не проверял ESP32, но обычно ядра без AVR имеют фиктивные " F ()", " PSTR ()", "PROGMEM", "pgm_read_ что угодно" и т. Д., Которые просто помещают вещи в единственное адресное пространство, которое у них есть. Таким образом, существует своего рода понятие их наличия в том смысле, что они не вызывают ошибок для непризнанных идентификаторов., @timemage

@timemage Это именно то, что я думаю, да. В приведенной выше программе это может ничего не делать, но это просто неправильно. Данные должны быть считаны в обычную оперативную память., @PMF

@PMF, ах, это имеет смысл. Раньше я читал это по-другому., @timemage

@PMF да, извините, это была ошибка, которую я действительно пропустил. Однако удаление PROGMEM ничего не изменило. @timemage что ты имеешь в виду? Я успешно использовал "malloc" в другом коде, поэтому я могу подтвердить, что он распознается ядром ESP32., @user2959923

Является ли sizeof(символ без знака) = 1? Если бы это было не так, это объяснило бы катастрофу., @PMF

@PMF, для char, независимо от того, как указана (или не указана) знаковость, sizeof всегда будет давать 1. Это своего рода определение в стандарте. В принципе, как указано, sizeof возвращает количество символов, необходимых для представления чего-либо, где "символ" становится синонимом "байта", независимо от того, являются ли байты октетами или нет., @timemage

@timemage Спасибо за объяснение. Я больше не был в этом уверен. Я в основном программист на C#, где sizeof(char) - это просто _not_ 1, потому что строки по определению являются юникодом., @PMF


1 ответ


1

Решив, я немного изменил метод.

В цикле я читаю часть файла и записываю его в открытый удаленный файл FTP, а затем закрываю оба в конце цикла

// открыть FTP-соединение
ftp.OpenConnection();

// открыть локальный файл
FileUpload = fopen("/sdcard/img.png", "r+");

// инициализировать тип файла
ftp.InitFile("Тип I");

// изменить удаленный каталог
ftp.ChangeWorkDir("/Shared/");

// определение имени удаленного файла
String strName = "img.png";

// преобразовать в char
const char *f_name = strName.c_str();

// создать удаленный файл с заданным именем
ftp.NewFile( f_name );

// read all file in a loop
unsigned char buff[64];
while(!feof(FileUpload)){
    
    // чтение части файла
    fread(buff, sizeof(char), sizeof(buff), FileUpload);

    // запишите эту часть в удаленный файл
    ftp.WriteData(buff, sizeof(buff));
}

// закройте как локальный, так и удаленный файл
fclose(FileUpload);
ftp.CloseFile();
,

Было бы хорошо получить более конкретное объяснение причины. Одна вещь, которую вы сделали, - это отошли от " malloc ()", что является хорошей идеей. В процессе, похоже, вы исправили утечку памяти, которая была вызвана отсутствием соответствующего вызова " free(). Вы также избавились от ftp.WriteData(в, sizeof(в));`, который записывал только первые 4 четырех байта того, на что указывает "в", потому что "размер (в)" - это размер указателя, а не размер выделенного блока., @timemage

Для того, что остается, существует вероятность сбоя "fopen", которую вам, вероятно, следует проверить, прежде чем пытаться использовать "Загрузка файлов". Когда дело доходит до "феофа" и "фреда", важно знать, что "феоф" не говорит вам, когда вы находитесь в конце файла. Он сообщает вам, когда вы пытались прочитать **после** конца файла. Поэтому, чтобы исправить цикл, вам нужно сначала попробовать "fread", а затем проверить, не произошло ли это из-за условия EOF, а затем либо разорвать цикл, либо записать данные., @timemage

@timemage Причина этого решения заключается в том, что строка "fread(in, sizeof(символ без знака), размер, загрузка файла);" пытается прочитать элементы "size" ("размер" - это полный размер файла) и пытается поместить эти элементы в символ "in", но символ " in "имеет только 4 байта, независимо от функции "malloc"., @user2959923

Если вы когда-нибудь появитесь на freenode, я бы рассказал, что я об этом думаю. Для этого я просто скажу, что, по-моему, вам, возможно, нужно будет еще немного посмотреть на него., @timemage