Arduino. Запись данных и вывод на сервер

Altik
Offline
Зарегистрирован: 28.01.2021

Добрый день, уважаемые форумчане!

Я работаю над проектом: запись данных акселерометра на micro SD карту и отправка данных на web server. Использую arduino uno и Ethernet Shield + micro SD

Застрял на этапе объединения транслирования данных с карты на сервер и записи показателей

Ниже представлен мой код, я пока только начинающий так что готов к критике и предложениям!)

А вопрос такой, как объединить постоянную запись данных и выкладку файлов (Я сохраняю данные в CSV формат) списком на сервер например раз в 15 минут  или постоянно.

Мой код сейчас создает каждый раз новый файл с новым именем,  из  setup вносит в файл  Gx, Gy, Gz

и далее как раз и нужны показания подряд: то есть я по отдельности сделал это и нужно как-то интегрировать в loop  это: file = SD.open(fileName, FILE_WRITE);

  if (file)
  {
    String header = ("Gx,      Gy,      Gz");
    file.println(header);
    Serial.print(F("opened: "));
    Serial.println(fileName);
    file.close();
    Serial.println(header);
  }
  else
  {
    Serial.println("EROR111");
  }

 

#include <SD.h>
#include <SPI.h>
#include <Ethernet.h>
#define FILE_BASE_NAME "Data_"

byte mac[] = { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };  // change if necessary
byte ip[] = { 192, 168, 1, 177 };                     // change if necessary
EthernetServer server(80);

int CS_pin = 10;
int pow_pin = 8;
int xPin = A0;
int yPin = A1;
int zPin = A2;

float Vmax = 5.0;

float x0 = 1.71;
float y0 = 1.69;
float z0 = 1.68;

float sens_x = 0.35;
float sens_y = 0.35;
float sens_z = 0.35;

#define SDCARD_CS 4
File file;

const uint8_t BASE_NAME_SIZE = sizeof(FILE_BASE_NAME) - 1;
char fileName[] = FILE_BASE_NAME "00.csv";

//long id =  1;

#if defined(ESP8266)
// default for ESPressif
#define WIZ_CS 15
#elif defined(ESP32)
#define WIZ_CS 33
#elif defined(ARDUINO_STM32_FEATHER)
// default for WICED
#define WIZ_CS PB4
#elif defined(TEENSYDUINO)
#define WIZ_CS 10
#elif defined(ARDUINO_FEATHER52)
#define WIZ_CS 11
#else   // default for 328p, 32u4 and m0
#define WIZ_CS 10
#endif

// store error strings in flash to save RAM
#define error(s) error_P(PSTR(s))

void error_P(const char* str) {
  Serial.print(F("error: "));
  Serial.println(str);

  while (1);
}

void setup() {
  Serial.begin(9600);
  Serial.println("Распознавание SD карты");
  pinMode(CS_pin, OUTPUT);
  pinMode(pow_pin, HIGH);

  if (!SD.begin(SDCARD_CS)) {
    error("card.init failed!");
  }
  Serial.println("Card ready");

  Ethernet.init(WIZ_CS);
  // give the ethernet module time to boot up
  delay(1000);
  // start the Ethernet connection
  // Use the fixed IP specified. If you want to use DHCP first
  //   then switch the Ethernet.begin statements
  Ethernet.begin(mac, ip);
  // try to congifure using DHCP address instead of IP:
  //  Ethernet.begin(mac);

  // print the Ethernet board/shield's IP address to Serial monitor
  Serial.print(F("My IP address: "));
  Serial.println(Ethernet.localIP());

  server.begin();

  while (SD.exists(fileName)) {
    if (fileName[BASE_NAME_SIZE + 1] != '9') {
      fileName[BASE_NAME_SIZE + 1]++;
    } else if (fileName[BASE_NAME_SIZE] != '9') {
      fileName[BASE_NAME_SIZE + 1] = '0';
      fileName[BASE_NAME_SIZE]++;
    } else {
      Serial.println(F("Can't create file name"));
      return;
    }
  }
  file = SD.open(fileName, FILE_WRITE);
  if (file)
  {
    String header = ("Gx,      Gy,      Gz");
    file.println(header);
    Serial.print(F("opened: "));
    Serial.println(fileName);
    file.close();
    Serial.println(header);
  }
  else
  {
    Serial.println("EROR111");
  }

}

void ListFiles(EthernetClient client, uint8_t flags, File dir) {
  client.println("<ul>");
  while (true) {
    File entry = dir.openNextFile();

    // done if past last used entry
    if (! entry) {
      // no more files
      break;
    }

    // print any indent spaces
    client.print("<li><a href=\"");
    client.print(entry.name());
    if (entry.isDirectory()) {
      client.println("/");
    }
    client.print("\">");

    // print file name with possible blank fill
    client.print(entry.name());
    if (entry.isDirectory()) {
      client.println("/");
    }

    client.print("</a>");
    /*
        // print modify date/time if requested
        if (flags & LS_DATE) {
           dir.printFatDate(p.lastWriteDate);
           client.print(' ');
           dir.printFatTime(p.lastWriteTime);
        }
        // print size if requested
        if (!DIR_IS_SUBDIR(&p) && (flags & LS_SIZE)) {
          client.print(' ');
          client.print(p.fileSize);
        }
    */
    client.println("</li>");
    entry.close();
  }
  client.println("</ul>");
}

// How big our line buffer should be. 100 is plenty!
#define BUFSIZ 100



void loop()
{
  char clientline[BUFSIZ];
  char name[17];
  int index = 0;

  
  int value_x = analogRead(xPin);
  int value_y = analogRead(yPin);
  int value_z = analogRead(zPin);

  float Gx = (value_x * Vmax / 1024.0 - x0) / sens_x;
  float Gy = (value_y * Vmax / 1024.0 - y0) / sens_y;
  float Gz = (value_z * Vmax / 1024.0 - z0) / sens_z;

  

  EthernetClient client = server.available();
  if (client) {
    // an http request ends with a blank line
    boolean current_line_is_blank = true;

    // reset the input buffer
    index = 0;

    while (client.connected()) {
      if (client.available()) {
        char c = client.read();

        // If it isn't a new line, add the character to the buffer
        if (c != '\n' && c != '\r') {
          clientline[index] = c;
          index++;
          // are we too big for the buffer? start tossing out data
          if (index >= BUFSIZ)
            index = BUFSIZ - 1;

          // continue to read more data!
          continue;
        }

        // got a \n or \r new line, which means the string is done
        clientline[index] = 0;

        // Print it out for debugging
        Serial.println(clientline);

        // Look for substring such as a request to get the file
        if (strstr(clientline, "GET /") != 0) {
          // this time no space after the /, so a sub-file!
          char *filename;

          filename = clientline + 5; // look after the "GET /" (5 chars)  *******
          // a little trick, look for the " HTTP/1.1" string and
          // turn the first character of the substring into a 0 to clear it out.
          (strstr(clientline, " HTTP"))[0] = 0;

          if (filename[strlen(filename) - 1] == '/') { // Trim a directory filename
            filename[strlen(filename) - 1] = 0;      //  as Open throws error with trailing /
          }

          Serial.print(F("Web request for: ")); Serial.println(filename);  // print the file we want

          File file = SD.open(filename, O_READ);
          if ( file == 0 ) {  // Opening the file with return code of 0 is an error in SDFile.open
            client.println("HTTP/1.1 404 Not Found");
            client.println("Content-Type: text/html");
            client.println();
            client.println("<h2>File Not Found!</h2>");
            client.println("<br><h3>Couldn't open the File!</h3>");
            break;
          }

          Serial.println("File Opened!");

          client.println("HTTP/1.1 200 OK");
          if (file.isDirectory()) {
            Serial.println("is a directory");
            //file.close();
            client.println("Content-Type: text/html");
            client.println();
            client.print("<h2>Files in /");
            client.print(filename);
            client.println(":</h2>");
            ListFiles(client, LS_SIZE, file);
            file.close();
          } else { // Any non-directory clicked, server will send file to client for download
            client.println("Content-Type: application/octet-stream");
            client.println();

            char file_buffer[16];
            int avail;
            while (avail = file.available()) {
              int to_read = min(avail, 16);
              if (to_read != file.read(file_buffer, to_read)) {
                break;
              }
              // uncomment the serial to debug (slow!)
              //Serial.write((char)c);
              client.write(file_buffer, to_read);
            }
            file.close();
          }
        } else {
          // everything else is a 404
          client.println("HTTP/1.1 404 Not Found");
          client.println("Content-Type: text/html");
          client.println();
          client.println("<h2>File Not Found!</h2>");
        }
        break;
      }
    }
    // give the web browser time to receive the data
    delay(20);
    client.stop();
  }

}



void printDirectory(File dir, int numTabs) {
  while (true) {
    File entry =  dir.openNextFile();
    if (! entry) {
      // no more files
      break;
    }
    for (uint8_t i = 0; i < numTabs; i++) {
      Serial.print('\t');
    }
    Serial.print(entry.name());
    if (entry.isDirectory()) {
      Serial.println("/");
      printDirectory(entry, numTabs + 1);
    } else {
      // files have sizes, directories do not
      Serial.print("\t\t");
      Serial.println(entry.size(), DEC);
    }
    entry.close();


  }
}

 

Altik
Offline
Зарегистрирован: 28.01.2021

Вывод COM порта

 

GET Распознавание SD карты
Card ready
My IP address: 192.168.1.177
opened: Data_34.csv
Gx,      Gy,      Gz
 
 
У нас создался файл и в нем есть вот эти заголовочки, и после них нужны значения:
Gx,      Gy,      Gz
1.12     0.60    -0.09
1.01     0.12    -0.48
 
sadman41
Offline
Зарегистрирован: 19.10.2016

Какого типа помощи Вы ожидаете? Совета? Вот совет: выделите нужный фрагмент кода, скопируйте в loop()

Altik
Offline
Зарегистрирован: 28.01.2021

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

sadman41
Offline
Зарегистрирован: 19.10.2016

Altik пишет:

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

Как же Вы представляете себе корректную работу?

Altik
Offline
Зарегистрирован: 28.01.2021

Файл должен создаваться и заголовки должны быть, но потом он должен не закрываться,а принимать входные значения и записывать в этот файл Gx, Gy, Gz. Я могу скинуть корректный скетч просто акселерометра, где все это реализовано, но без сервера.

Грубо говоря у меня по отдельности все работает: и сервер, и данные пишутся как надо, а вот при объединении начинаются проблемы

sadman41
Offline
Зарегистрирован: 19.10.2016

Лично мне не нужен "скетч просто акселерометра". 

Сначала определитесь при каких условиях должен открываться и закрываться файл. А потом уже в готовый код вписывайте фрагменты, открывающие и закрывающие (или не закрывающие) файл.

Altik
Offline
Зарегистрирован: 28.01.2021

После  загрузки скетча на ардуино должно происходить следующее: Создаваться файл CSV, туда пишутся заголовки Gx, Gy, Gz, далее в этот файл записываются показания акселерометра по каждой из осей до тех пор, пока программа работает и должна быть параллельно отправка на сервер. Как только перестает или сбой, будет создаваться новый файл. Я так понимаю, что из-за моей этой бесконечной записи в файл показаний, как раз и не получается объединить это

sadman41
Offline
Зарегистрирован: 19.10.2016

Если файл закрывать не надо, значит в setup() не следует вызывать функцию file.close() - логично?

Если в loop() необходимо писать данные, то нужно в нем вызывать file.close() - тоже логично?

Насчёт отправки на сервер я ничего не понял, т.к. в коде ничего подобного не наблюдаю.

Altik
Offline
Зарегистрирован: 28.01.2021

А про сервер: С помощью Ethernet Shield подключаюсь локальному серверу 192.168.1.177 и там показываются данные SD карты, при этом в моем скетче при открывании монитора порта создается новый файл и на сервере при обновлении страницы он появляется

 

Задумка такова, чтобы поставить ардуино и она писала данные на sd карту, параллельно транслируя на сервер и чтобы можно было их скачать

sadman41
Offline
Зарегистрирован: 19.10.2016

Altik пишет:

А про сервер: С помощью Ethernet Shield подключаюсь локальному серверу 192.168.1.177 и там показываются данные SD карты, 

Это не так. Код говорит о том, что с помощью Ethernet Shield  вы на Arduino создаете Web-сервер и обслуживаете подключающихся к Arduino клиентов, отдавая им файлы с SD.

Altik
Offline
Зарегистрирован: 28.01.2021

Да, вы очень хорошо сформулировали, именно так, спасибо

И вот объединить этот процесс и запись показаний не удается

Altik
Offline
Зарегистрирован: 28.01.2021

У меня  они по отдельности отлично работают

Altik
Offline
Зарегистрирован: 28.01.2021

sadman41 пишет:

Если файл закрывать не надо, значит в setup() не следует вызывать функцию file.close() - логично?

Если в loop() необходимо писать данные, то нужно в нем вызывать file.close() - тоже логично?

Насчёт отправки на сервер я ничего не понял, т.к. в коде ничего подобного не наблюдаю.

 

Если в setup убрать file.close(), то на сервере будет писаться "Файлы не найдены"

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Altik пишет:

Да, вы очень хорошо сформулировали, именно так, спасибо

И вот объединить этот процесс и запись показаний не удается

Мне кажется, это потому, что Вы сами не понимаете, как у Вас должна работать программа.

Постарайтесь то, что Вы описали в сообщении №7, записать в виде последовательных шагов алгоритма. По-русски.

Altik
Offline
Зарегистрирован: 28.01.2021

andriano пишет:

Altik пишет:

Да, вы очень хорошо сформулировали, именно так, спасибо

И вот объединить этот процесс и запись показаний не удается

Мне кажется, это потому, что Вы сами не понимаете, как у Вас должна работать программа.

Постарайтесь то, что Вы описали в сообщении №7, записать в виде последовательных шагов алгоритма. По-русски.

 

Я так и делал, и я считаю, что когда это подставлял в loop  все выглядело логично, но не работает и я не понимаю причины

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Altik пишет:

Я так и делал, и я считаю, что когда это подставлял в loop  все выглядело логично, но не работает и я не понимаю причины

Мы не видели, что именно Вы делали, поэтому тоже не понимаем причины.

KPG
Offline
Зарегистрирован: 09.06.2019

andriano пишет:

Altik пишет:

Я так и делал, и я считаю, что когда это подставлял в loop  все выглядело логично, но не работает и я не понимаю причины

Мы не видели, что именно Вы делали, поэтому тоже не понимаем причины.

 А, Вы. проверьте что он делал и доложите о результатах проверки здесь.

Altik
Offline
Зарегистрирован: 28.01.2021

И вот нужно как-то объединить, но не выходит

sadman41
Offline
Зарегистрирован: 19.10.2016

Ардуина не является устройством, осуществляющим за полчаса любые фантазии людей без подготовки. Некоторым так и не удается осуществить желаемое в экономически оправданные сроки.
Так что учтите - простая, на первый взгляд, вещь может выливаться написание сотен и тысяч строк кода, дни самостоятельной отладки и т.п.

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

Altik
Offline
Зарегистрирован: 28.01.2021

sadman41 пишет:
Ардуина не является устройством, осуществляющим за полчаса любые фантазии людей без подготовки. Некоторым так и не удается осуществить желаемое в экономически оправданные сроки. Так что учтите - простая, на первый взгляд, вещь может выливаться написание сотен и тысяч строк кода, дни самостоятельной отладки и т.п. В данном случае, с вашим алгоритмом, видимо придется каждый раз при обслуживании клиента закрывать файл, а потом открывать его заново.

 

Может есть какой-то альтернативный метод/идея?

andriano
andriano аватар
Offline
Зарегистрирован: 20.06.2015

Altik пишет:

И вот нужно как-то объединить, но не выходит

Задача изначально некорректная: объединить два скетча в общем случае невозможно.

А если нужно объединить функции двух скетчей, то это делается следующим образом:

1. Выясняется алгоритм работы каждого из скетчей.

2. Записывается алгоритм для решения требуемой задачи.

3. Код на основании 2 пишется с нуля.

sadman41
Offline
Зарегистрирован: 19.10.2016

Альтернативных методов решения задачи, неизвестной решающему - мильярд. Попросите шведа посоветовать вам десерт - будете есть сюрстреминг с клюквенным соусом.