• Witaj na Forum Arduino Polska! Zapraszamy do rejestracji!
  • Znajdziesz tutaj wiele informacji na temat hardware / software.
Witaj! Logowanie Rejestracja


Ocena wątku:
  • 0 głosów - średnia: 0
  • 1
  • 2
  • 3
  • 4
  • 5
Zapis pomiarów na karcie SD
#1
Cześć,
Temat dotyczący zapisów wartości na karcie SD był wielokrotnie sam znalazłem wiele takich rozwiązań każdy podobny do siebie. Jednak u mnie problem jest z samym zgraniem kilku funkcji programu.
Mój projekt ma służyć do pomiarów wiatru za pomocą czujnika wiatru czaszowego z kontaktronem. Do tego wykorzystany będzie wyświetlacz OLED do wyświetlania aktualnej prędkości wiatru. A na końcu pomiar ma być zapisywany na karcie SD w pliku o formacie .txt.


Problem pojawia się w przypadku dodania zapisów dla modułu karty microSD. Program zaczyna się wieszać i nie wykonuje pomiarów oraz nic nie wyświetla.

Czy jest może ktoś kto rzucił by okiem na kod może da się jakoś zgrać wszystkie funkcje programu? 


Kod:
#include <SD.h>
#include <Adafruit_SSD1306.h>
#include <Adafruit_GFX.h>
#include <SPI.h>

#define OLED_RESET 4
Adafruit_SSD1306 display(OLED_RESET);

File plik;

//Średnica anemometru
float diameter = 2.75;
float mph; //Utworzenie zmiennej mile/godzinę
float kmh; //Utowrzenie zmiennej km/h

// Odczyt obrotĂłw (RPM)
int half_revolution_time = 0; //Utworzenie zmiennej przechowującej
int rpm = 0; //Utworzenie zmiennej RPM (obroty)
unsigned long lastmillis = 0; //Utworzenie zmiennej long lastmilis

void rpm_fan() {
 unsigned long static last_event = 0;
 if (millis() - last_event < 5) {   //debouncing
   return;
 }
 half_revolution_time = (millis() - last_event);
 last_event = millis();
}

void setup() {
 Serial.begin(9600); // Uruchomienie portu szeregowego z prędkością 9600
 pinMode(2, INPUT_PULLUP); // Aktywowanie rezystora podciągającego na pinie 8

 attachInterrupt(digitalPinToInterrupt(2), rpm_fan, FALLING);
 // Przypisanie funkcji rpm_fan przerwania zewnętrznego
 // Przerwanie zostanie wykonane jeśli stan zmieni się z wysokiego na niski (Falling)
 
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
 display.clearDisplay();

 Serial.print("Włączanie karty SD...");
 if(SD.begin(4)){
   
   Serial.println("Błąd karty SD");
   return;
 }
 Serial.println("Karta SD gotowa");
 }
void loop() {

 if (millis() - lastmillis >= 1000) {
   //Aktualizuj co sekundę, będzie to rĂłwnoznaczne z odczytem częstotliwości (Hz)

   lastmillis = millis();          // Aktualizacja lastmillis
   
   noInterrupts();                   // W trakcie kalkulacji wyłącz obsługę przerwań
   rpm = (30000 / half_revolution_time) ;      
   interrupts() ; //Przywróć przerwania
   
   mph = diameter / 12 * 3.14 * rpm * 60 / 5280;//Odczyt prędkości wiatru w milach/godzinę
   mph = mph * 3.5; // Kalibracja błędu odczytu, wartość naleĹźy dobrać we własnym zakresie
   kmh = mph * 1.609;// Zamiana mil/godzinę na km/h
 }
   Serial.print("KMH=\t"); //Przesłanie odczytanych danych do portu szeregowego
   Serial.println(kmh);
   Serial.println();
   
   plik = SD.open("pomiar.txt", FILE_WRITE);
   if (plik){
     plik.println(kmh);
     plik.close();
 }
   display.clearDisplay();
   display.setTextSize(2);
   display.setTextColor(WHITE);
   display.setCursor(0,0);
   display.println("Wind speed");
   display.setTextSize(2);
   display.setCursor(0,17);
   display.print(kmh);
   display.setTextSize(2);
   display.println("km/h");
   display.display();
}
 
Odpowiedź
#2
Arduino to taki Windows, system wielozadaniowy, pod warunkiem, ze do każdego zadania użyjesz osobnego komputera.

Przeanalizuj kod i oszacuj jak często zapisujesz dane na karcie SD.
Pomagam (nie prowadzę kursów), tylko, gdy w poście załączone są niezbędne materiały (kod, schemat) a pytający zna podstawy.

Na kod z Delayi i/lub bez WDT to nie kod, to DEMO!
Kod wymaga komentarzy!




 
Odpowiedź
#3
Jak tego mozna dokonac? Myślałem o uzaleznieniu zapisów na karcie SD od wykonania pomiaru. Tylko nie wiem jak to opisać w kodzie
 
Odpowiedź
#4
Klauzula sumienia nie pozwala mi dać gotowca. Sprawdź jak jest realizowany (w nieudolny sposób) pomiar częstotliwości. Przyjrzyj się fragmentowi w okolicach:
Cytat: //Aktualizuj co sekundę, będzie to równoznaczne z odczytem częstotliwości (Hz)
Tam funkcja jest realizowana co mniej więcej 1 sekundę (dlatego nieudolna realizacja). W ten sam sposób, możesz realizować odświeżanie LCD np 5 razy na sekundę, zapis na SD np co minutę.


PS
Jak to ma działać 24/h, to i tak wyłożysz się na problemach z millis (co za młot to zrobił tak jak zrobił?) ale jak rozwiążesz swój problem to napiszę jak przewalczyć millis a najlepiej całe Ardino posłać w diabły i zrobić to jak należy .
Pomagam (nie prowadzę kursów), tylko, gdy w poście załączone są niezbędne materiały (kod, schemat) a pytający zna podstawy.

Na kod z Delayi i/lub bez WDT to nie kod, to DEMO!
Kod wymaga komentarzy!




 
Odpowiedź
#5
Pobaw się jakimś kursem od A-Z, np. na Forbocie. Nie rób zadań mechanicznie, bez zrozumienia, bo każde kolejne to będzie wyższy płotek do przeskoczenia i tylko się poobijasz. Dokonujesz zapisu na kartę w każdym obiegu pętli, nieważne czy masz nowe dane czy nie. Tak optymalnie to można próbować zacząć od zapisu raz na 1 minutę, a mln/s to mocne nieporozumienie. Pomyśl ile PC potrzebuje czasu po włożeniu karty SD na jakąś reakcję, wyświetlenie zawartości, odczyt pierwszego pliku. Użyj funkcji podobnej do millis, micros() i sprawdź ile taki zapis trwa. Jeśli interesują Cię takie szczegółowe dane co 1s to możesz je sobie wrzucać do tablicy i zapisywać na kartę średnią z 10min, min i max. Tutaj byłoby warto przerwać wykonywanie pomiarów na czas zapisu, tak by zapis został wykonany prawidłowo. Do odmierzania czasu z millis używaj zmiennych i stałych typu UL, będzie działać wbrew krakaniom osób, którym się tego nie chciało sprawdzić, do końca świata lub odcięcia zasilania. Ten sam błąd przy używaniu oleda.
 
Odpowiedź
#6
(15-07-2018, 19:25)kaczakat napisał(a): Do odmierzania czasu z millis używaj zmiennych  i stałych typu UL,

Stała UL gwarantuje, że będzie traktowana jako unsigned long ale nie zmieni rozmiaru licznika używanego przez millis z 32 na 64 bity. Używanie millis to rozrzutność. Trzeba zawsze używać zmiennych 32 bit. Jak jest to jeden licznik czy dwa nie ma problemu ale ja jest ich 50? 50x4bajty = 200bajtów czyli aż 10% ram popularnego UNO. W 90% przypadków odmierza się czasy do 64 sekund i wystarczą na to 2 bajty. Bardzo często czasy są krótsze niż 250ms co rozwiązuje bajt. Nie chodzi tylko o zajętość ram ale i o czas wykonania operacji. AVR to nie ARM i porównanie 32bit to kilkanaście rozkazów (tak "na oko" 16) a nie 2 czy 3. Pierwszą rzeczą jaką należałoby zrobić w Arduino to wywalić chory millis i ustawić przerwania dokładnie co 1ms. To otwiera drogę do timerów odliczających "po bożemu" od zadanej wartości do zera o dowolnym rozmiarze dopasowanym do wymagań aplikacji.

(15-07-2018, 19:25)kaczakat napisał(a): którym się tego nie chciało sprawdzić
Po co sprawdzać i ratować chore rozwiązania? Są lepsze, sprawdzone metody, o jednej napisałem wcześniej.
Pomagam (nie prowadzę kursów), tylko, gdy w poście załączone są niezbędne materiały (kod, schemat) a pytający zna podstawy.

Na kod z Delayi i/lub bez WDT to nie kod, to DEMO!
Kod wymaga komentarzy!




 
Odpowiedź
#7
Millis odpowiednio używane działa bez zarzutu. Nie następuje żaden koniec świata ani po 49 ani 54 dniach jak Pan wspomina kiedy się da, bo licznik się przepełnia. Nic więc nie trzeba ratować. Jak ktoś pisze program, gdzie walczy o us to Arduino z AVR nie jest drogą do sukcesu, ale większość programów dla początkujących tego nie wymaga. Do zapisu na kartę nie trzeba wymyślać koła na nowo, tylko zrozumieć jak działa to co dali w Arduino "dla ludu". Jak sawka234 tak będzie zapisywał nawet co 1s na kartę SD to ona nawet miesiąc nie przeżyje, jeśli po poprzednich "testach" w ogóle jeszcze zipie, przecież puścił zapis na flash ile loop() pozwalał.
 
Odpowiedź
#8
(15-07-2018, 21:53)kaczakat napisał(a): Millis odpowiednio używane działa bez zarzutu.
Ten co wymyślił millis musiał być kretynem albo naćpany. Jeśli chciałbym zrealizować swoje timera 8 czy 16 bit, i wepnę się w przerwanie od timera0 to muszę robić jakieś chore korekty co 125ms. Wszystko dlatego, że ustawione jest przerwanie od przepełnienia a ni porównania w trybie CTC. Większego idiotyzmu jeszcze nie widziałem.

Problem przepełniania jest o czym niejedne już się dowiedział. Co się stanie jak czekając np 60 minut, użyję rozkazów:
x=millis() + 60*60*1000;
if( millis() > x )
a millis będzie równe 0xFFFFFF00 ?

Czy zajechał kartę? Wątpię. Pętla loop nie wykonywała się tysiące czy miliony razy na sekundę. Otworzenie i zamknięcie pliku jest czasochłonne. Strzelam, że zajmuje z pół sekundy albo i dłużej.
Zapisywany plik jest mały a karty mają mechanizm zapobiegający zapisom tych samych bloków. Powiedzmy, że karta jest mała 2GB. Sektor duży 4kB. Daje to ok 500'000bloków. Zakładając zapis co 500ms daje to 250'000 sekund = 4100min = 69godzin = ok 3 dni. Po 3 dniach bloki będą zajmowane od początku. Standardowy flash to 10'000 zapisów czyli 30'000dni = ok 82lata. Dobrze liczę?
Niech będzie, ze zapisywał co 100ms w co wątpię to da jakieś 16 lat.
No dobrze, hyper Arduino z roku 3000, zapis co 10ms. 1,6 roku.
Miał szansę zajeździć kartę?
Trzeba pamiętać, że blok raczej nie ma 4kB a 512 bajtów, co oznacza, ze czas życia karty należy pomnożyć przez 8.

Dobrze policzyłem?

A kolega sprawdził:
(15-07-2018, 21:53)kaczakat napisał(a): Tak optymalnie to można próbować zacząć od zapisu raz na 1 minutę, a mln/s to mocne nieporozumienie.
?
Jeśli tak to proszę o wyniki, ile do tego miliona brakuje?
Pomagam (nie prowadzę kursów), tylko, gdy w poście załączone są niezbędne materiały (kod, schemat) a pytający zna podstawy.

Na kod z Delayi i/lub bez WDT to nie kod, to DEMO!
Kod wymaga komentarzy!




 
Odpowiedź
#9
W co się pakujemy używając millis?
Porównanie 2 liczb 32-bit na AVR w postaci funkcji:
Kod:
__SREG__ = 0x3f
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__tmp_reg__ = 0
square(unsigned long, unsigned long):
        push r29
        push r28
        in r28,__SP_L__
        in r29,__SP_H__
        sbiw r28,8
        in __tmp_reg__,__SREG__
        cli
        out __SP_H__,r29
        out __SREG__,__tmp_reg__
        out __SP_L__,r28
        std Y+1,r22
        std Y+2,r23
        std Y+3,r24
        std Y+4,r25
        std Y+5,r18
        std Y+6,r19
        std Y+7,r20
        std Y+8,r21
        ldd r18,Y+1
        ldd r19,Y+2
        ldd r20,Y+3
        ldd r21,Y+4
        ldd r24,Y+5
        ldd r25,Y+6
        ldd r26,Y+7
        ldd r27,Y+8
        cp r24,r18
        cpc r25,r19
        cpc r26,r20
        cpc r27,r21
        brsh .L2
        ldi r24,lo8(1)
        ldi r25,hi8(1)
        ldi r26,hlo8(1)
        ldi r27,hhi8(1)
        rjmp .L3
.L2:
        ldi r24,lo8(0)
        ldi r25,hi8(0)
        ldi r26,hlo8(0)
        ldi r27,hhi8(0)
.L3:
        mov r22,r24
        mov r23,r25
        mov r24,r26
        mov r25,r27
        adiw r28,8
        in __tmp_reg__,__SREG__
        cli
        out __SP_H__,r29
        out __SREG__,__tmp_reg__
        out __SP_L__,r28
        pop r28
        pop r29
        ret
przy czym istotny kod to:
Kod:
ldd r18,Y+1
        ldd r19,Y+2
        ldd r20,Y+3
        ldd r21,Y+4
        ldd r24,Y+5
        ldd r25,Y+6
        ldd r26,Y+7
        ldd r27,Y+8
        cp r24,r18
        cpc r25,r19
        cpc r26,r20
        cpc r27,r21
        brsh .L2

.L2:
Kto chce niech liczy ile to zajmuje pamięci i jak "szybko" się wykonuje.
To samo dla ARM:
Kod:
square(unsigned long, unsigned long):
        str     fp, [sp, #-4]!
        add     fp, sp, #0
        sub     sp, sp, #12
        str     r0, [fp, #-8]
        str     r1, [fp, #-12]
        ldr     r2, [fp, #-8]
        ldr     r3, [fp, #-12]
        cmp     r2, r3
        bls     .L2
        mov     r3, #1
        b       .L3
.L2:
        mov     r3, #0
.L3:
        mov     r0, r3
        add     sp, fp, #0
        ldr     fp, [sp], #4
        bx      lr
przy czym istotny kod to:
Kod:
ldr     r2, [fp, #-8]
        ldr     r3, [fp, #-12]
        cmp     r2, r3
        bls     .L2

.L2:

Gdy porównujemy 16 bit dla AVR:
Kod:
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__tmp_reg__ = 0
square(unsigned int, unsigned int):
        push r28
        push r29
        rcall .
        rcall .
        in r28,__SP_L__
        in r29,__SP_H__
        std Y+2,r25
        std Y+1,r24
        std Y+4,r23
        std Y+3,r22
        ldd r18,Y+1
        ldd r19,Y+2
        ldd r24,Y+3
        ldd r25,Y+4
        cp r24,r18
        cpc r25,r19
        brsh .L2
        ldi r24,lo8(1)
        ldi r25,hi8(1)
        rjmp .L3
.L2:
        ldi r24,lo8(0)
        ldi r25,hi8(0)
.L3:
        pop __tmp_reg__
        pop __tmp_reg__
        pop __tmp_reg__
        pop __tmp_reg__
        pop r29
        pop r28
        ret
przy czym samo porównanie:
Kod:
ldd r18,Y+1
        ldd r19,Y+2
        ldd r24,Y+3
        ldd r25,Y+4
        cp r24,r18
        cpc r25,r19
        brsh .L2

.L2:
liczby 8 bit:
Kod:
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__tmp_reg__ = 0
square(unsigned char, unsigned char):
        push r28
        push r29
        rcall .
        in r28,__SP_L__
        in r29,__SP_H__
        std Y+1,r24
        std Y+2,r22
        ldd r25,Y+1
        ldd r24,Y+2
        cp r24,r25
        brsh .L2
        ldi r24,lo8(1)
        rjmp .L3
.L2:
        ldi r24,lo8(0)
.L3:
        pop __tmp_reg__
        pop __tmp_reg__
        pop r29
        pop r28
        ret
samo porównanie:
Kod:
ldd r25,Y+1
        ldd r24,Y+2
        cp r24,r25
        brsh .L2

.L2:

Czy wykazałem, że używanie millis jest chorym pomysłem?


PS
Porównania 16 i 8 bit dla ARM nie pokazałem, bo to uC 32-bit i czy 8 czy 32-bit liczba rozkazów będzie taka sama.
Pomagam (nie prowadzę kursów), tylko, gdy w poście załączone są niezbędne materiały (kod, schemat) a pytający zna podstawy.

Na kod z Delayi i/lub bez WDT to nie kod, to DEMO!
Kod wymaga komentarzy!




 
Odpowiedź
#10
Dużo nikomu niepotrzebnej pracy, a ja dalej nie widziałem Pana super bibliotek. Właśnie o to chodzi, ten sam kod logowania w Arduino mogę odpalić na UNO, ale również na ARM i ESP. Płytka UNO na Allegro to koszt około 20zł, tyle samo płytka z WIFI NODE ESP8266 lub 40zł za ESP32. Jak kogoś interesuje to wie, że przy obliczeniach na int32 ESP32 jest tysiące razy szybszy od UNO czy setki razy we floatach, no i w ogóle działa z double.
Millis jest dla amatorów i Arduino, zadziała również a ARM/ESP i nie muszę znać budowy tych procesorów by zarządzać czasem w programie. Komuś nie pasuje to nawet nie musi używać millis, setup i loop, czy bootloadera, ani nawet Arduino IDE. Proszę mi nie kazać uczyć Pana co należy zrobić by odliczyć milisami 60minut bez przepełniania zmiennych. Jak ktoś chce sobie głębiej grzebać w trzewiach kwarcu to nie musi używać Arduino, po co Pan się ma denerwować.
Żyjemy w dobie Internetu, nie muszę sam testować by wiedzieć - zapis linijki tekstu w wierszu na SD może trwać od 10 do 40ms (atmega328 8MHz), prędkość zależy też od karty. Ale co tam, przetestowałem też u siebie, na karcie Nokia 512MB około 11 ms na UNO i 6ms na ESP32. także proszę sobie wprowadzić poprawki do obliczeń i zamienić swoją bibliotekę SD na arduinową Big Grin, kto by się spodziewał, że system dla amatorów i dzieciaków to kop w dupę o 1000 lat?. No OK, jakiś hipersuper Kingston 32GB SDHC 90;45MB/s miał czasy ponad 30ms. Mi karty SD padały w telefonach również po kilkukrotnym zapisie pracując w normalnym trybie jako magazyn fotek czy multimediów, oczywiście po typowej gwarancji ale pracując już właściwie w odczycie, nie wierzę więc w żadne bajeczne 82 lata wyliczone na papierze Pana czy koreańskich/chińskich inżynierów. Uśpienie w ESP liczone jest w us w uint64_t, można z tego korzystać i się cieszyć, albo narzekać i zacząć pisać swoje biblioteki "porządnie" od zera do wszystkiego - życzę owocnej pracy, może będzie mniej czasu na bezsensowne bicie piany.
 
Odpowiedź
  


Skocz do:


Przeglądający: 1 gości