Получение даты и времени создания файла SD.h

SWDjon
Offline
Зарегистрирован: 27.08.2022

Всем доброго времени. Подскажите пожалуйста как получить дату и время создания файла?

#include <SPI.h>
#include <SD.h>
#include <Wire.h>
#include <TimeLib.h>
#include <DS1307RTC.h>
File file;
unsigned long prev = 0;
tmElements_t tm;

void dateTime(uint16_t* date, uint16_t* time)
{
*date = FAT_DATE(tmYearToCalendar(tm.Year), tm.Month, tm.Day);
*time = FAT_TIME(tm.Hour, tm.Minute, tm.Second);
}

 

// Файл создан из примера
Serial.begin(9600);
SdFile::dateTimeCallback(dateTime);
if (!SD.begin(10))
{
Serial.println("SD.begin failed");
while (1);
}
file = SD.open("TEST_IT.TXT", FILE_WRITE);
file.println("Good write!");
file.close();
Serial.println("Done");

Вот только ума ни приложу - чем прочитать атрибуты файла, в числе которых особенно интересует время создания, изменения файла. 4 часа поиска в интернете не привели к результату.

v258
v258 аватар
Offline
Зарегистрирован: 25.05.2020

Ты явно хочешь странного. Как ардуина сможет получить доступ к файловой системе компа?

SWDjon
Offline
Зарегистрирован: 27.08.2022

Благодарю за внимание. Видимо я недостаточно раскрыл суть вопроса, меня интересует не ПК, а SD карта на которую ардуина сама же и пишет и читает, вопрос - как узнать, когда она это записала, прочитала или изменила.

SAB
Offline
Зарегистрирован: 27.12.2016

Кто вам мешает писать лог с датой и временем в ещё один файл на ту же SD

SWDjon
Offline
Зарегистрирован: 27.08.2022

Благодарю за предложение. Вижу необходимость дополнить свой вопрос - требуется решение без "обходных путей", в том числе писать в файл дату его создания. Основанием является использование созданных файлов для анализа во внешней программе.

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

А чем не угодили штатные средства самой библиотеки SD? С ними что-то не так?

У структуры directoryEntry есть целая куча полей для даты и времени создания, последней модификации и последнего доступа. Вот они:

uint8_t creationTimeTenths;
uint16_t creationTime;
uint16_t creationDate;
uint16_t lastAccessDate;
uint16_t lastWriteTime;
uint16_t lastWriteDate;

Читаете каталог и для каждого файла имеете всю информацию.

Или я чего- то недопонял?

SWDjon
Offline
Зарегистрирован: 27.08.2022

Премного благодарен! Это как раз то что  я и искал, только вот структуру эту для отдельного файла... Ещё раз спасибо! Пошёл искать описание по структуре как её правильно прочесть.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

У класса SdFile есть метод dirEntry. Вот он эту структуру и читает.

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

ТС, ты туда заходил вообще??? Там все с примерами!...

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

BOOM пишет:

ТС, ты туда заходил вообще??? Там все с примерами!...

Там установка, а не чтение, а это две большие разницы.

А с чтением там не так просто. Метод, о котором я говорил, относится к SdFile , а SdFile член в классе File - private, до него не так легко добраться.

Можно плюнуть и перенести его в public, а можно (если не хочется трогать системную библиотеку и помнить об этом всякий раз, когда IDE обновляешь) просто не пользоваться верхним уровнем (классом File) а всё делать на честном SdFile. Вот, например, печать имён файлов и их дат/времени создания из корневого каталога. Подкаталоги делать не стал, чтобы ТС мозг не выносить рекурсией.

#include <SPI.h>
#include <SD.h>

const uint8_t pinCS = 4;

//
// Печать даты и времени создания файла из элемента каталога de
//
void printDateTime(const dir_t & de) {
// ДД.ММ.ГГГГ ЧЧ:ММ:СС
// 12345678901234567890
	char buffer[20];
	sprintf(buffer, "%02d.%02d.%04d %02d:%02d:%02d",
		FAT_DAY(de.creationDate), 
		FAT_MONTH(de.creationDate), 
		FAT_YEAR(de.creationDate),
		FAT_HOUR(de.creationTime),
		FAT_MINUTE(de.creationTime),
		FAT_SECOND(de.creationTime));
	Serial.println(buffer);
}

//
// Печать имени файла из элемента каталога de
//	в хвост добавляются пробелы до ширины 14
//
void printName(const dir_t & de) {
	int8_t w = 0;
	for (uint8_t i = 0; i < 11; i++) {
		if (de.name[i] == ' ') continue;
		if (i == 8) { Serial.print('.'); w++; }
		Serial.write(de.name[i]);
		w++;
	}
  if (DIR_IS_SUBDIR(&de)) { Serial.print('/'); w++; }
  for (; w < 14; w++) Serial.print(' ');
}

//
//
//
void setup(void) {
	Serial.begin(9600);
	//
	// Подключаемся к карте
	Sd2Card card;
	if (!card.init(SPI_HALF_SPEED, pinCS)) {
		Serial.println("Initialization failed. Check wirings!");
		return;
	} else {
		Serial.println("Card is ready!");
	}
	//
	//	Открываем файловую систему
	SdVolume volume;
	if (!volume.init(card)) {
		Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
		return;
	} else {
		Serial.println("File system is okay!");
	}

	//
	//	Открываем корневой каталог
	SdFile root;
	root.openRoot(volume);

	//
	//	Печатаем корневой каталог (имена и даты файлов)
	dir_t p;
	while (root.readDir(& p)) {
		if (p.name[0] == DIR_NAME_FREE) break;
		// Пропустить удалённые фалы и "." с ".."
		if (p.name[0] == DIR_NAME_DELETED || p.name[0] == '.') continue;
		// Печатаем только файлы и названия директорий
		if (!DIR_IS_FILE_OR_SUBDIR(& p)) continue;
		printName(p); // Печатаем имя файла
		printDateTime(p);	// Печатаем дату и время
	}
	root.close();
}

void loop(void) {}

Результат:

Card is ready!
File system is okay!
IMAGE.BMP     27.08.2022 19:05:08
KAKA1.TXT     27.08.2022 19:11:14
KAKA2.TXT     27.08.2022 19:16:14
KAKA3.TXT     27.08.2022 19:21:14
KAKA4.TXT     27.08.2022 19:26:14
SWDjon
Offline
Зарегистрирован: 27.08.2022

Огромнейшее спасибо! Ваш код заработал фактически сразу (только пин SS поменял в соответствии со своим подключением)

Благодаря Вам, Евгений, вопрос в целом решён.

Есть только один момент, но возможно это уже в аппаратной части, или же нет... А именно - есть две разные sd-card, обе в FAT32, но на одной код с использованием SdFat работает, а на другой нет. При этом в коде без использования SdFat ситуация обратная.

Конкретно:

// При такой инициализации одна карта работает, вторая нет
Sd2Card card;
if (!card.init()) {
Serial.println("Initialization failed. Check wirings!");
return;
} else {
Serial.println("Card is ready!");
}
// При такой инициализации ситауция обратная.
if (!SD.begin(10))
{
Serial.println("SD.begin failed");
while (1);
}

PS: Сейчас я конечно же пока буду работать с той картой которая работает с вашим кодом. Тем не менее, всё таки на долгие зимние вечера, может есть какие нибудь предложения, или возможно уже сталкивались с подобной ситуацией и есть решение?

Скорости SPI_HALF_SPEED, SPI_QUARTER_SPEE менял, но как и предполагал - результат 0;

 

BOOM
BOOM аватар
Offline
Зарегистрирован: 14.11.2018

Евгений Петрович, там в #1 «официальный пример»:

void printTimestamps(SdFile& f) {
  dir_t d;
  if (!f.dirEntry(&d)) error("f.dirEntry failed");

  cout << pstr("Creation: ");
  f.printFatDate(d.creationDate);
 // и тд
}

Но уже не суть...

SWDjon
Offline
Зарегистрирован: 27.08.2022

По картам:

Фото карт и подключения на Yandex.disk

 

 

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

BOOM пишет:

Евгений Петрович, там в #1 «официальный пример»:

Это да. Там в поставке даже есть пример listfiles - который всю директорию печатает, но ни в этом примере, ни в том, до дат ручками не добираются, вся спрятано.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

SWDjon пишет:
А именно - есть две разные sd-card, обе в FAT32, но на одной код с использованием SdFat работает, а на другой нет. При этом в коде без использования SdFat ситуация обратная.

Ну, в таких случаях надо смотреть в чём разница. Если Вы откроете исходник, Вы увидите, что метод begin не содержит никакого волшебства, он вызывает тот же init и, заодно, открыват том и корневой каталог (т.е. то же самое, что в противном случае Вы делаете руками). Вот он:

  boolean SDClass::begin(uint8_t csPin) {
    if (root.isOpen()) {
      root.close();
    }

    /*
      Performs the initialisation required by the sdfatlib library.
      Return true if initialization succeeds, false otherwise.
    */
    return card.init(SPI_HALF_SPEED, csPin) &&
           volume.init(card) &&
           root.openRoot(volume);
  }

Как видите, если begin ломается, значит ломается что-то из трёх: card.init, volume.init или root.openRoot.

Поставьте ту карту, на которой ломается begin и попробуйте эти три метода вызвать руками с проверкой, чтобы увидеть кто из них ломается. Тогда уже можно будет смотреть почему он ломается.

Собственно, так всегда и разбираются.

И ещё, пожалуйста, вставляйте код правильно.