Функция записи SD нужна помощь

dachnik
Offline
Зарегистрирован: 26.07.2013

Подскажите в чём моя ошибка? Пишу такую функцию записи на карту SD данных. В первом параметре String file_name передаю с каким файлом нужно работать функции. В результате выдаёт ошибку:

*.ino: In function 'int sd_write(String, String, String, float)':
*:1026: error: no matching function for call to 'SDClass::open(String&, int)'
C:\Program Files (x86)\Arduino\libraries\SD\src/SD.h:74: note: candidates are: File SDClass::open(const char*, uint8_t)

 

int sd_write (String file_name, String now_date, String now_time, float value)
{

  myFile = SD.open(file_name , FILE_WRITE);
  
  // if the file opened okay, write to it:
  if (myFile) {
    Serial.print("Writing to test.txt...");
    myFile.println("testing 1, 2, 3.");
	// close the file:
    myFile.close();
    Serial.println("done.");
  } else {
    // if the file didn't open, print an error:
    Serial.println("error opening test.txt");
  }
 
}

Где мой косяк? непонимаю на что ругается...

maksim
Offline
Зарегистрирован: 12.02.2012

SDClass::open(const char*, uint8_t) принимает в качестве аргумента указатель на string, а вы пытаетесь скормить ей объект String.

char f_n[file_name.length()+1];
file_name.toCharArray(f_n, file_name.length());
myFile = SD.open(f_n , FILE_WRITE);

 

dachnik
Offline
Зарегистрирован: 26.07.2013

Спасибо!

ites
Offline
Зарегистрирован: 26.12.2013

maksim пишет:

char f_n[file_name.length()+1];

Это gcc-расширение стандарта?

maksim
Offline
Зарегистрирован: 12.02.2012

Нет, это объявление статического массива. Можно просто объявить указатель.

ites
Offline
Зарегистрирован: 26.12.2013

maksim пишет:

Нет, это объявление статического массива. Можно просто объявить указатель.

Размерность массива динамическая тут. И он не статический, а выделяется на стеке. Это C99 стандарт такое разрешил, раньше это было доступно только как gcc-расширение. Я на С++ перешёл раньше, так что новшества C99 упустил. Сорри за шум, просто глаз зацепился.

ites
Offline
Зарегистрирован: 26.12.2013

Посмотрел внимательно на класс String и не понял, почему у него нет метода c_str(), который решил бы вопрос топикстартера. Посмотрев ещё более внимательно, считаю этот класс мог бы быть пооптимальнее.

ites
Offline
Зарегистрирован: 26.12.2013

maksim пишет:

SDClass::open(const char*, uint8_t) принимает в качестве аргумента указатель на string, а вы пытаетесь скормить ей объект String.

char f_n[file_name.length()+1];
file_name.toCharArray(f_n, file_name.length());
myFile = SD.open(f_n , FILE_WRITE);

 

Кстати, я немного опоздал с предложением добавить c_str в класс String: https://github.com/jabbervorx/Arduino/commit/19e4d387258eb4dcd323fe10c9cceb0671ef8e03

 

dachnik
Offline
Зарегистрирован: 26.07.2013

maksim пишет:

SDClass::open(const char*, uint8_t) принимает в качестве аргумента указатель на string, а вы пытаетесь скормить ей объект String.

char f_n[file_name.length()+1];
file_name.toCharArray(f_n, file_name.length());
myFile = SD.open(f_n , FILE_WRITE);

 

Почему-то при создании съедает последний символ в имени файла. Cохраняет в виде *.tx  а не *.txt

maksim
Offline
Зарегистрирован: 12.02.2012

попробуйте так

char *f_n;
file_name.toCharArray(f_n, file_name.length()+1);
myFile = SD.open(f_n , FILE_WRITE);

 

dachnik
Offline
Зарегистрирован: 26.07.2013

неа, непрокатывает.

смотрю что выводит в serial:

Serial.println("error opening sd card. File: ");
Serial.print(f_n);

На выходе:

initialization done.
error opening sd card. File: 

Походу пустое имя файла получается и по этому error.

 

ites
Offline
Зарегистрирован: 26.12.2013

maksim пишет:

попробуйте так

char *f_n;
file_name.toCharArray(f_n, file_name.length()+1);
myFile = SD.open(f_n , FILE_WRITE);

 

Так нельзя делать. Куда указывает f_n? Указатель не инициализирован, это прямой путь перезаписать поверх важных данных или вообще никуда не записать, если указывает на несуществующую область памяти.

char f_n[file_name.length()+1];
file_name.toCharArray(f_n, file_name.length()+1);
myFile = SD.open(f_n , FILE_WRITE);

Секрет в том, что второй аргумент toCharArray -- размер буфера, который был сначала правильно указан как file_name.length()+1.

dachnik
Offline
Зарегистрирован: 26.07.2013

Спасибо ещё раз!. Теперь полное имя файла)))

всёравно недогоняю чего Вы написали:

char f_n[file_name.length()+1];
file_name.toCharArray(f_n, file_name.length()+1);

Первая строчка создаём массив символов. размерность массива задаётся из длинны file_name.

а это что?

length()+1 

и это тоже непонятно... =) Если Вам несложно объясните нубу?)

file_name.toCharArray(f_n, file_name.length()+1);
ites
Offline
Зарегистрирован: 26.12.2013

dachnik пишет:

Спасибо ещё раз!. Теперь полное имя файла)))

всёравно недогоняю чего Вы написали:

char f_n[file_name.length()+1];
file_name.toCharArray(f_n, file_name.length()+1);

Первая строчка создаём массив символов. размерность массива задаётся из длинны file_name.

а это что?

length()+1 

и это тоже непонятно... =) Если Вам несложно объясните нубу?)

file_name.toCharArray(f_n, file_name.length()+1);

Первая строка выделяет массив символов на стеке функции. Размерность массива равна длине имени файла плюс один символ для признака конца строки '\0' (подробнее можно прочитать про ascii-z строки в гугле).

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

Поскольку для признака конца строки тоже требуется место, то размер буфера должен быть на один символ больше, чем длина строки.

В новой версии библиотеки есть функция c_str(), с помощью которой можно было бы написать код проще:

myFile = SD.open(file_name.c_str() , FILE_WRITE);

Но эта библиотека пока что в статусе беты.

dachnik
Offline
Зарегистрирован: 26.07.2013

А сколько максимум символов может иметь имя файла? столкнулся с тем, что имя файла вместе с расширением имеет 14 символов, никак не хотелось создаваться и сохраняться на сдишку. Уменьшил имя файла до 11 символов - создаётся и записывается...

maksim
Offline
Зарегистрирован: 12.02.2012

12345678.123

ites
Offline
Зарегистрирован: 26.12.2013

dachnik пишет:

А сколько максимум символов может иметь имя файла? столкнулся с тем, что имя файла вместе с расширением имеет 14 символов, никак не хотелось создаваться и сохраняться на сдишку. Уменьшил имя файла до 11 символов - создаётся и записывается...

Если внимательно прочтитать описание библиотеки, то там написано, что они ограничились поддержкой коротких имён только. Формат короткого имени для FAT 8+3,  как написал Максим.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

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

dirEEP = EEPROM.read(0); // тут хранится имя папки
  fileNumberEEP = EEPROM.read(1); // тут хранится имя файла
  fileNumberEEP = fileNumberEEP++;
  if (fileNumberEEP == 256){
    fileNumberEEP = 0;
    dirEEP = dirEEP++;
    if (dirEEP == 256){
      dirEEP = 0;
    }
    EEPROM.write(0, dirEEP);
  }
  EEPROM.write(1,fileNumberEEP);

Но потом когда я захотел задать имя создаваймого файла на флешке fileNumberEEP

File dataFile = SD.open(fileNumberEEP, FILE_WRITE);

ругаецо:

001.ino: In function 'void setup()':
001.ino:58: warning: operation on 'fileNumberEEP' may be undefined
001.ino:61: warning: operation on 'dirEEP' may be undefined
001.ino: In function 'void loop()':
001:103: error: invalid conversion from 'byte' to 'const char*'
001:103: error: initializing argument 1 of 'File SDClass::open(const char*, uint8_t)'

 

И ещё, под шумок помогите создавать файлы не 0...255 а 0.csv...255.csv

Вот весь код:

/*
  SD card datalogger
 
 * analog sensors on analog ins 0, 1, and 2
 * SD card attached to SPI bus as follows:
 ** MOSI - pin 11
 ** MISO - pin 12
 ** CLK - pin 13
 ** CS - pin 4
 	 
 */

static int vccRead(byte us =250) {
  ADMUX = 1<<REFS0; // опорное напряжение - Vcc
  ADMUX |= 0x0E;    // объект измерения - внутренний источник
  // стабилизированного напряжения 1.1В
  delayMicroseconds(us);

  ADCSRA |= 1<<ADSC;         // запуск АЦ-преобразования
  while(ADCSRA & (1<<ADSC)); // и ожидание его завершения
  word x = ADC;
  return x ? (1100L * 1023) / x : -1;
}

#include <EEPROM.h>
#include <SD.h>
#include <avr/sleep.h> // здесь описаны режимы сна
#include <avr/power.h>
#define analogPin 0

const int chipSelect = 4;
byte dirEEP = 0;
byte fileNumberEEP = 0;
boolean stateLed1 = LOW; // 
boolean stateLed2 = LOW; //
boolean stateLed3 = LOW; //
byte counter1 = 0;

void setup()
{
  pinMode(10, OUTPUT);  // питалово 1
  pinMode(9, OUTPUT);  // питалово 2
  pinMode(8, OUTPUT); // жёлтый светодиод
  pinMode(7, OUTPUT); // зелёный  
  pinMode(6, OUTPUT); // белый
  digitalWrite(10, HIGH); // подадим питание флешки
  digitalWrite(9, HIGH); // одного порта маловато

  for(byte led_test = 6; led_test <= 8; led_test++){
    digitalWrite(led_test, HIGH);   // turn the LED on (HIGH is the voltage level)
    delay(500); 
    digitalWrite(led_test, LOW);   // turn the LED on (HIGH is the voltage level)
    delay(500); 
  }

  dirEEP = EEPROM.read(0); // тут хранится имя папки
  fileNumberEEP = EEPROM.read(1); // тут хранится имя файла
  fileNumberEEP = fileNumberEEP++;
  if (fileNumberEEP == 256){
    fileNumberEEP = 0;
    dirEEP = dirEEP++;
    if (dirEEP == 256){
      dirEEP = 0;
    }
    EEPROM.write(0, dirEEP);
  }
  EEPROM.write(1,fileNumberEEP);

  Serial.begin(9600);
  /*while (!Serial) {
   ; // wait for serial port to connect. Needed for Leonardo only
   }*/

  Serial.print("Initializing SD card...");
  // make sure that the default chip select pin is set to
  // output, even if you don't use it:
  //pinMode(10, OUTPUT);

  // see if the card is present and can be initialized:
  if (!SD.begin(chipSelect)) {
    Serial.println("Card failed, or not present");
    return;
  }
  Serial.println("card initialized.");

}

void loop()
{
  digitalWrite(6, stateLed1);
  digitalWrite(7, stateLed2);
  digitalWrite(8, stateLed3);

  int sensor = analogReadOversampled(analogPin);
  int Vcc = VccReadOversampled();
  if (sensor > 10) {
    stateLed2 = HIGH; 
  }
  else{
    stateLed2 = LOW;
  }

  File dataFile = SD.open(fileNumberEEP, FILE_WRITE); // "datalog.csv"
  if (dataFile) {
    dataFile.print(Vcc);
    dataFile.print(", ");
    dataFile.println(sensor);
    dataFile.close();
    Serial.print(sensor);
    Serial.print(", ");
    Serial.println(Vcc);
    stateLed1 = HIGH;
  }  
  else {
    Serial.println("error opening datalog.csv");
    stateLed3 = !stateLed3;
    Serial.println(Vcc);  // временно
    counter1++;
    if(counter1 == 255){
      Serial.print("Zzz...");
      delay(50);
      system_sleep_2S();
    }
  } 
  //Serial.println(fileNumberEEP + ".csv");
}

unsigned int analogReadOversampled(byte analogChannel)  {
  unsigned long aSum = 0;   // the sum of all analog readings
  for(unsigned int i = 256; i > 0; i--){ 
    aSum = aSum + analogRead(analogChannel); // read and sum 16 ADС probes
  }
  return aSum >> 7;   // ..
}

unsigned int VccReadOversampled()  {
  unsigned long aSum = 0;   // the sum of all analog readings
  for(unsigned int i = 16; i > 0; i--){ 
    aSum = aSum + vccRead(); // read and sum 16 ADС probes
  }
  return aSum >> 4;   // ..
}

void system_sleep_2S(){
  //wdt_reset(); // сброс
  //wdt_enable(WDTO_2S); // разрешение ватчдога раз в 2с
  //WDTCR |= _BV(WDE); // разрешение прерываний по ватчдогу (иначе будет резет)!
  //sei(); // разрешение прерывания
  ADCSRA &= ~(1 << ADEN);  // отключаем АЦП
  set_sleep_mode(SLEEP_MODE_PWR_DOWN); // если спать - то на полную
  while(1) {
    sleep_enable(); // разрешаем сон
    sleep_cpu(); // спать!
  }

}

Прошу прощения за гавнокод, но я по другому не умею.

NeiroN
NeiroN аватар
Offline
Зарегистрирован: 15.06.2013

Arduino\hardware\tools\avr\doc\avr-libc\avr-libc-user-manual.pdf или здесь(онлайн версия) http://nongnu.org/avr-libc/user-manual/group__avr__stdio.html

Я там нашол много полезных функций.

char fname[20];
sprintf(fname,"log-%d.csv", 0...255); //Обычная
sprintf_min(fname,"log-%d.csv", 0...255); // Меньше памяти ест
sprintf_P(fname,"log-%d.csv", 0...255); // Формат хранится в флеш памяти программы

 

 

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Ниасилю, знаю точно.

NeiroN
NeiroN аватар
Offline
Зарегистрирован: 15.06.2013

Сишник вам не пехепе!

Как попало писать - работать не будет.

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

А где %d это значит что туда мы будем пихать даные которые идут после комы?

HWman
HWman аватар
Offline
Зарегистрирован: 26.02.2013

Спасибо, работает.

Вот только не совсем разобрался с двумя другими принт эфами

sprintf_min(fname,"log-%d.csv", 0...255); // Меньше памяти ест
sprintf_P(fname,"log-%d.csv", 0...255); // Формат хранится в флеш памяти программы

Как можно есть меньше памяти?

А второй, если хранится во флеш памяти то это типо как EEPROM?

NeiroN
NeiroN аватар
Offline
Зарегистрирован: 15.06.2013

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

http://www.cplusplus.com/reference/cstdio/sprintf/

настоятельно рекомендую читать документацию чтобы не было глупых вопросов.

sprintf_P константа формата в PROGMEM