Логгер данных на карту памяти

mefi73
Offline
Зарегистрирован: 13.02.2015

создаю устройство по типу "умного дома", решил прикрутить логгер состояния датчиков на СД карте. хочу сделать так что б каждый день создавался новый файл, и в течении суток в него записывались данные. для этого прикрутил карту СД и часы реального времени. лог в файл пишется, но я никак не могу заставить ардуину создавать каждый день новый файл. пробывал функцию rtc.getDateStr(), но файл не создается. при этом функция rtc.getTimeStr() исправно создает новый файл каждую секунду. пробывал применить в качестве имени файла переменную, 

FileName = rtc.getDateStr();
File dataFile = SD.open(FileName);

но компилятор ругается на функцию SD.open(FileName), типа нельзя в качестве имени задавать эту переменную, у нее не тот тип данный. второй день бьюсь, ничего не выходит.

Gippopotam
Gippopotam аватар
Offline
Зарегистрирован: 12.09.2014

Какой тип этой переменной?

 

 

а еще "в лоб" можно так:

int d = currentDay;

int m = currentMonth;

int y = currentYear;

String filename = (String)y + (String)m + (String)d;

 

savuniversal
Offline
Зарегистрирован: 03.05.2012

Может кто подскажет - как определить свободное пространство на частично заполненной карте. Необходимо для ограничения количества записей в FileLoger. Поиск помучил.

mefi73
Offline
Зарегистрирован: 13.02.2015

делал тип переменной String и char, в обоих случаях компилятор ругается.

про свободное пространство

вот скетч из примера CardInfo библиотеки SD

/*
  SD card test 
   
 This example shows how use the utility libraries on which the'
 SD library is based in order to get info about your SD card.
 Very useful for testing a card when you're not sure whether its working or not.
 	
 The circuit:
  * SD card attached to SPI bus as follows:
 ** MOSI - pin 11 on Arduino Uno/Duemilanove/Diecimila
 ** MISO - pin 12 on Arduino Uno/Duemilanove/Diecimila
 ** CLK - pin 13 on Arduino Uno/Duemilanove/Diecimila
 ** CS - depends on your SD card shield or module. 
 		Pin 4 used here for consistency with other Arduino examples

 
 created  28 Mar 2011
 by Limor Fried 
 modified 9 Apr 2012
 by Tom Igoe
 */
 // include the SD library:
#include <SD.h>

// set up variables using the SD utility library functions:
Sd2Card card;
SdVolume volume;
SdFile root;

// change this to match your SD shield or module;
// Arduino Ethernet shield: pin 4
// Adafruit SD shields and modules: pin 10
// Sparkfun SD shield: pin 8
const int chipSelect = 53;    

void setup()
{
 // Open serial communications and wait for port to open:
  Serial.begin(9600);
   while (!Serial) {
    ; // wait for serial port to connect. Needed for Leonardo only
  }


  Serial.print("\nInitializing SD card...");
  // On the Ethernet Shield, CS is pin 4. It's set as an output by default.
  // Note that even if it's not used as the CS pin, the hardware SS pin 
  // (10 on most Arduino boards, 53 on the Mega) must be left as an output 
  // or the SD library functions will not work. 
  pinMode(53, OUTPUT);     // change this to 53 on a mega


  // we'll use the initialization code from the utility libraries
  // since we're just testing if the card is working!
  if (!card.init(SPI_HALF_SPEED, chipSelect)) {
    Serial.println("initialization failed. Things to check:");
    Serial.println("* is a card is inserted?");
    Serial.println("* Is your wiring correct?");
    Serial.println("* did you change the chipSelect pin to match your shield or module?");
    return;
  } else {
   Serial.println("Wiring is correct and a card is present."); 
  }

  // print the type of card
  Serial.print("\nCard type: ");
  switch(card.type()) {
    case SD_CARD_TYPE_SD1:
      Serial.println("SD1");
      break;
    case SD_CARD_TYPE_SD2:
      Serial.println("SD2");
      break;
    case SD_CARD_TYPE_SDHC:
      Serial.println("SDHC");
      break;
    default:
      Serial.println("Unknown");
  }

  // Now we will try to open the 'volume'/'partition' - it should be FAT16 or FAT32
  if (!volume.init(card)) {
    Serial.println("Could not find FAT16/FAT32 partition.\nMake sure you've formatted the card");
    return;
  }


  // print the type and size of the first FAT-type volume
  uint32_t volumesize;
  Serial.print("\nVolume type is FAT");
  Serial.println(volume.fatType(), DEC);
  Serial.println();
  
  volumesize = volume.blocksPerCluster();    // clusters are collections of blocks
  volumesize *= volume.clusterCount();       // we'll have a lot of clusters
  volumesize *= 512;                            // SD card blocks are always 512 bytes
  Serial.print("Volume size (bytes): ");
  Serial.println(volumesize);
  Serial.print("Volume size (Kbytes): ");
  volumesize /= 1024;
  Serial.println(volumesize);
  Serial.print("Volume size (Mbytes): ");
  volumesize /= 1024;
  Serial.println(volumesize);

  
  Serial.println("\nFiles found on the card (name, date and size in bytes): ");
  root.openRoot(volume);
  
  // list all files in the card with date and size
  root.ls(LS_R | LS_DATE | LS_SIZE);
}


void loop(void) {
  
}

в этом примере определяется размер всей памяти флеш-карты и выводится список файлов с их размером. так может взять общий размер карты и вычесть размеры всех файлов? решение говнокодное но в лоб.

savuniversal
Offline
Зарегистрирован: 03.05.2012

 

>// list all files in the card with date and size

>111 root.ls(LS_R | LS_DATE | LS_SIZE);

вывод только на Serial? Как считать в переменную?

Penni
Penni аватар
Offline
Зарегистрирован: 18.01.2015

Просто так не перенаправить, но всегда можно сделать своё казино с блэкджеком и шшшироким функционалом :)

Надо только поправить два файла и будет счастье. А файлы такие: SdFat.h и SdFile.cpp

В SdFat.h после строки void ls(uint8_t flags = 0, uint8_t indent = 0); вставляем строку uint32_t UsedDiskSpace(uint8_t flags = 0);

Далее идём в файл SdFile.cpp и после метода void SdFile::ls(uint8_t flags, uint8_t indent) вставляем свою функцию. ТОЛЬКО НЕ ПОСЛЕ ЭТОЙ СТРОКИ А ИМЕННО ПОСЛЕ ВСЕГО МЕТОДА НЕ ТРОГАЯ ШТАТНУЮ ФУНКЦИЮ

uint32_t SdFile::UsedDiskSpace(uint8_t flags)
{
  dir_t* p;
  uint32_t NonFreeSpace = 0;
  rewind();
  while ((p = readDirCache()))
  {
    // done if past last used entry
    if (p->name[0] == DIR_NAME_FREE) break;
    // skip deleted entry and entries for . and  ..
    if (p->name[0] == DIR_NAME_DELETED || p->name[0] == '.') continue;
    // only list subdirectories and files
    if (!DIR_IS_FILE_OR_SUBDIR(p)) continue;
    // print size if requested
    if (!DIR_IS_SUBDIR(p) && (flags & LS_SIZE))
      NonFreeSpace += p->fileSize;
    // list subdirectory content if requested
    if ((flags & LS_R) && DIR_IS_SUBDIR(p))
    {
      uint16_t index = curPosition()/32 - 1;
      SdFile s;
      if (s.open(this, index, O_READ))
        NonFreeSpace += s.UsedDiskSpace(flags);
      seekSet(32 * (index + 1));
    }
  }
  return NonFreeSpace;
}

На всякий случай делайте копии всех файлов! Только не оставляйте в той же папке либо расширение меняйте, не имя а именно расширение.

Ну а далее используя стандартный пример:

// print the type and size of the first FAT-type volume
  uint32_t volumesize;
  uint32_t uds;
  Serial.print("\nVolume type is FAT");
  Serial.println(volume.fatType(), DEC);
  Serial.println();

  volumesize = volume.blocksPerCluster();    // clusters are collections of blocks
  volumesize *= volume.clusterCount();       // we'll have a lot of clusters
  volumesize *= 512;                            // SD card blocks are always 512 bytes
  Serial.print("Volume size (bytes): ");
  Serial.println(volumesize);

  root.openRoot(volume);

  uds = root.UsedDiskSpace(LS_R | LS_DATE | LS_SIZE);
  Serial.print("Free Disk Space = ");
  Serial.print(volumesize-uds);
  Serial.println(" bytes");

Вот собственно так я извращался когда мне надо было узнать свободное место. Может и проще как-то можно, но мне надо было на коленки за пол часа сделать и курить мануалы было некогда :)

По скорости работает немного быстрее чем ls. Мою 2гиговую карту на которой свободно 118 мегабайт и на которой куча мелких файлов и папок обсчитывает за пару секунд где-то.

Gippopotam
Gippopotam аватар
Offline
Зарегистрирован: 12.09.2014

mefi73 пишет:

делал тип переменной String и char, в обоих случаях компилятор ругается.

А почему char? Почему не указатель на массив символов, как это подсказывает здравый смысл?

Я вот не поленился, нашел библиотеку, в которой есть данная функция (DS1307.rar).

а там английским по белому написано:

char    *getDateStr(uint8_t slformat=FORMAT_LONG, uint8_t eformat=FORMAT_LITTLEENDIAN, char divider='.');

Догадываетесь, почему вам никто не ответил сразу и конкретно?

mefi73
Offline
Зарегистрирован: 13.02.2015

запутался я с этими типами данных, и ни одного толкового мануала на русском не нашел. 

savuniversal
Offline
Зарегистрирован: 03.05.2012

2Penni

Благодарю. Работает.

Добавлю свои 5коп: считает размер файлов, но не размер на карте. Для моих задач ОК.

brokly
brokly аватар
Offline
Зарегистрирован: 08.02.2014

Что бы посчтать размер на карте нужно оклуглять размер каждого файла в большую сторону кратно 512 байтам. Расчет размера занятого места есть только в новой библиотеке, но там эта функция работает возмутительно медленно.

savuniversal
Offline
Зарегистрирован: 03.05.2012

brokly пишет:

Расчет размера занятого места есть только в новой библиотеке, но там эта функция работает возмутительно медленно.

SD.h? В какой новой?

Sirocco
Offline
Зарегистрирован: 28.09.2013

Люди, встречал кто логгер на ардуино с записью на SD? Ситуация срочная, нужно писать напряжения на 12 входах сразу. Дело в том, что есть модуль входов контроллера, если напряжение на входе 0-9В это логический ноль, если 9-30 это единица. Всё это работает вполне успешно, но в какой-то непонятный момент прилетает 220 или может 380 вольт и модуль целиком выгорает. Собственно момент невероятный и не возможный, но так происходит. Соответственно нужно мониторить несколько параметров сразу, чтоб потом восстановить ход событий и хотябы понять на какой именно вход прилетает импульс.  Быстродействие должно быть максимальное, иначе высоковольтный импульс можно пропустить.

Может у кого есть заготовки кода под это дело, или подобный проект встречали?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Sirocco, ардуино не столь быстра что бы отслеживать короткие сигналы на 12 каналах. Вам нужно шифратор на  FPGA делать, или купить готовый высокоскоростной анализатор на FPGA. + придётся сделать входной интерфейс с делителями и супрессорами для защиты самой платы контроля.

trembo
trembo аватар
Offline
Зарегистрирован: 08.04.2011

Или повесить на вход через мостик стабилитрон с пробивным на 50-100 Вольт, последовательно, с резистором для огранияения тока и вторым для привязки к земле..... Было бы два сигнала- посадить на прерывания.

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

trembo, кстати хорошая мысля! :)  Можно посадить на PCINT прерывания их  всего 3 штуки, но каждый по 8 входов захватывает.  Так что 2х прерываний хватит аж на 16 каналов. И по сработке прерывания прочитать весь порт, с которого пришёл "звоночек ". Да, пожалуй на ардуине можно. Одно в новинку -я не помню, что б кто нить тут на форуме пользовался PCINTовскими прерываниями. Но вроде лехко...)

Sirocco
Offline
Зарегистрирован: 28.09.2013

Ничё не понял. Дайте чёнить почитать на эту тему. А как сделать делитель я знаю. Программная часть для меня большая проблема.

Как вообще построить алгоритм? Ведь нужно, чтоб анализатор стоял хотябы пару суток, лучше пять. Но в то же время не пропустить событие... Наверное нужно очень быстро измерять, скажем 50 замеров писать в массив, смотреть нет ли резких отклонений между значениями, если нет - то искать среднее и писать на флеш, если есть, то писать только максимум. И так для каждого входа. По сути, если нет отклонений, то значение напряжения писать нет смысла, достаточно отмечать логический уровень. В принципе пофиг, тристадвадцатьвосьмых атмег у меня две горсти, если ресурсов не хватит то хоть одну на канал использовать можно. Только как-то объединить, чтоб на одну флеху писали в итоге, и время ещё логировать надо...

Есть идеи алгоритма? Как такие штуки вообще работают?

dimax
dimax аватар
Offline
Зарегистрирован: 25.12.2013

Sirocco, я предложил использовать "массовые" прерывания PCINT.  Про детали работы с ними можно почитать в даташите    73 страница или русскоязычным руководствам по контроллерам AVR мега. В логгере на SD карту не вижу смысла, событие как я понимаю происходит раз в несколько суток, что логгировать? Можно несколько байт иформации и в памяти подержать) В крайнем случае во внутреннюю еепром записать если очень хочется, но я б не стал. Могу накидать скелет программы. Сначала нужно выбрать соответствие будущим желаемым входам и их обозначениям pcint xx
(табличку подсмотреть  можно тут).  Для примера выберу от балды:
d9 pcint1 (PORTB.1)
d10 pcint2 (PORTB.2)
d11 pcint3 (PORTB.3)
d12 pcint4 (PORTB.4)
на pcint 5 висит светик, на на 6 и 7 кварц, пропустим их
a0 pcint8 (PORTC.0)
a1 pcint9  (PORTC.1)
a2 pcint10 (PORTC.2)
a3 pcint11 (PORTC.3)
a4 pcint12 (PORTC.4)
a5 pcint13 (PORTC.5)
ресеты и прочие сериалы пропустим , нам ещё 2 входа нужно , что бы было 12.
d4 pcint20 (PORTD.4)
d5 pcint21 (PORTD.5)

Ну можно и скетчик набросать:

//назначим байты куда класть данные по состоянию портов
volatile byte stat_0,stat_1,stat_2;
volatile bool flags; //и флажок заведём один на всех
void setup() {
//здесь желательно сделать на все нужные входы pinMode, я опущу эту процедуру.
PCICR|=(1<<PCIE2)|(1<<PCIE1)|(1<<PCIE0); //разрешим все три прерывания
PCMSK2|=(1<<PCINT21)|(1<<PCINT20); //тут всего два входа
PCMSK1|=(1<<PCINT13)|(1<<PCINT12)|(1<<PCINT11)|(1<<PCINT10)|(1<<PCINT9)|(1<<PCINT8); //а тут 6
PCMSK0|=(1<<PCINT4)|(1<<PCINT3)|(1<<PCINT2)|(1<<PCINT1); //  и ещё 4 входа
Serial.begin(9600);  
}

ISR (PCINT0_vect){
//сюда упадёт  по изменению на 1-4 PCINTах, прочитаем сразу весь порт, потом разберёмся
stat_0=PINB;  
flags=1; //флаг что детектор сработал

}
// аналогично на остальных
ISR (PCINT1_vect) {  stat_1=PINC;   flags=1; }
ISR (PCINT2_vect) { stat_2=PIND;   flags=1; }

void loop() { 
 // понятно, что если будут несколько быстрых преываний подряд, к примеру раз в единицы микросекунд, то узнать о них не выйдет, но нам это и не нужно, достаточно одной сработки. 
 
 if (flags){ // если кто-то сработал, то
    flags=0; //скинем флаг
    Serial.print ("Warning, level change after run, sec  ");
    Serial.println (millis()/1000);
    Serial.print ("PORTB ");
    Serial.println (stat_0,BIN);
// по идее тут нужно разобрать весь байт и вывести значения каждого порта, я опущу эту процедуру, просто выведем всё что есть. 
   Serial.print ("PORTC ");
    Serial.println (stat_1,BIN);
    Serial.print ("PORTD ");
    Serial.println (stat_0,BIN);
   } // enf  flags 
} // end loop

//Вот такой простой скетч выведет информацию о состоянии всех 12 входов и времени с момента запуска в секундах если хотя бы на одном входе изменился сигнал. 

 

Sirocco
Offline
Зарегистрирован: 28.09.2013

Спасиб за идею. Уже что-то получается.

savuniversal
Offline
Зарегистрирован: 03.05.2012

Sirocco пишет:

Люди, встречал кто логгер на ардуино с записью на SD? Ситуация срочная, нужно писать напряжения на 12 входах сразу. Дело в том, что есть модуль входов контроллера, если напряжение на входе 0-9В это логический ноль, если 9-30 это единица. Всё это работает вполне успешно, но в какой-то непонятный момент прилетает 220 или может 380 вольт и модуль целиком выгорает. Собственно момент невероятный и не возможный, но так происходит. Соответственно нужно мониторить несколько параметров сразу, чтоб потом восстановить ход событий и хотябы понять на какой именно вход прилетает импульс.  Быстродействие должно быть максимальное, иначе высоковольтный импульс можно пропустить.

Может у кого есть заготовки кода под это дело, или подобный проект встречали?

Если есть разъемы и мощность сигнала позволяет, я бы попробывал включить транзисторные оптроны по каждому сигналу. Какой выгорит тот и канал. Может и модуль спасет:). А если по земле, то и логгер не спасет, мне кажется.

Sirocco
Offline
Зарегистрирован: 28.09.2013

savuniversal пишет:
Если есть разъемы и мощность сигнала позволяет, я бы попробывал включить транзисторные оптроны по каждому сигналу. Какой выгорит тот и канал. Может и модуль спасет:). А если по земле, то и логгер не спасет, мне кажется.

Поставил на каждый вход по паре светодиодов smd, один моментом выгорает при напряжении выше 40V, другой при напряжении выше 250V. На некоторых входах выгорели светодиоды на 40V. Но ситуация загадочная. Без логгера не понять как она могла произойти. Там сложная релейная схема, много цепочек собирается. 

Как-то пару лет назад попадал в такую ситуацию. Сигнал с датчиков перепада давления на вентиляции шел в щит автоматики в соседнее помещение, шёл по тому же лотку, что и питание двигателя вентиляции 7,5КВт. Долго думал и тестил, искуственно создавал все аварийные ситуации, но не мог найти причину выгорания входов контроллера. Чисто случайно заметил. Оказалось, что в момент останова двигана пролетал сильный импульс, который наводил не хилое напряжение в слаботочных проводах. Причем этот импульс наводился только в тот момент, когда фильтр вентиляции был забит(реле в этот момент замыкалось отсылая сигнал об этом. Это не верное решение, но так было реализовано проектом.), и соответственно в штатной рабочей ситуации проблему было не отловить.

Но теперь другая ситуация...