Kurs Arduino – wykorzystanie danych w formacie CSV
W poprzednim odcinku kursu zrealizowaliśmy rejestrator (logger) w wersji wstępnej. W tym odcinku zrealizujemy rejestrator z zegarem RTC. Nauczymy się wykorzystywać i rozmaicie przedstawiać informacje zarejestrowane w formacie CSV.
W poprzednim odcinku kursu uzyskane dane przedstawiliśmy w postaci ładnego wykresu graficznego. To duże osiągnięcie, ale warto zwrócić uwagę na kilka istotnych szczegółów.
Jedna sprawa to czas. W ostatniej wersji rejestratora (A1102.ino) nie wykorzystywaliśmy modułu zegara czasu rzeczywistego, tylko zapisywaliśmy kolejne próbki w przypadkowych chwilach, wyznaczonych przez sumę opóźnień w pętli loop() programu (zawarta tam instrukcja opóźnienia delay(3000); nie dotyczy rejestracji, lecz wstrzymania związanego ze stanem niskim na wejściu A2).
Tylko w nielicznych przypadkach jest konieczne, by próbki były zbierane i zapisywane w ściśle określonych chwilach. W takich sytuacjach należałoby albo wykorzystać przerwania od układu RTC (DS3231) i wewnętrzny alarm, albo w programie Arduino dodać funkcję, która będzie tam sprawdzać czas odczytywany z DS3231 i odpowiednio, według potrzeb, przeprowadzać pomiar i rejestrację.
W zdecydowanej większości przypadków odstęp czasu między rejestracją kolejnych próbek nie ma istotnego znaczenia, natomiast ważne jest, kiedy poszczególne próbki były zbierane. Przykładowo jeżeli rejestrujemy przebieg temperatury za oknem, to nie ma znaczenia, czy próbki będziemy zbierać co 5, 10, 12 czy może co 15 minut. Ale ważne jest, żeby potem wiedzieć, jak zmieniała się temperatura w ciągu doby i na przykład o której godzinie osiągnęła minimum i maksimum. Aby to zrealizować, możemy wykorzystać ostatni program (A1102.ino) i tylko dodać doń instrukcje, zapisujące na karcie nie tylko wyniki pomiarów, ale też czas ich wykonania. Instrukcje dotyczące czasu możemy wziąć z programu – szkicu A0904.ino albo ze szkicu A1004.ino. Po połączeniu otrzymujemy wersję A1201.ino, dostępną tutaj =ZIP A12.
Układ z trzema czujnikami DS18B20 pozostaje taki sam, jaki był wcześniej (rysunek 1 i fotografia 2).
Po skompilowaniu i załadowaniu szkicu A1201.ino zebrałem troszkę danych, podgrzewając ręką poszczególne czujniki. Przebieg zmian temperatury nie ma żadnego znaczenia, ponieważ chcemy skupić się tylko na graficznym zobrazowaniu danych, a konkretnie by na osi poziomej wykresu uzyskać informację o czasie.
Zebrane dane zapisane zostały w pliku RSTRAT01.TXT. W związku z właściwościami Arduino liczby ułamkowe zapisywane są z kropką, a nie z przecinkiem. Odpowiednio „majstrując” w programie kalkulacyjnym i w systemie operacyjnym, można byłoby wykorzystać oryginalny taki plik, ale unikniemy wielu kłopotów, jeżeli z pliku .TXT zrobimy plik .csv z separatorami pól w postaci średników i z przecinkami w liczbach ułamkowych.
W tym celu choćby w Notatniku zmienimy najpierw wszystkie przecinki na średniki, a potem kropki na przecinki.
Aby pokazać pewne istotne problemy, po takiej konwersji zapisałem dwie wersje pliku .csv. Jedną „oryginalną” z literką T między datą a godziną, i drugą, gdzie literkę T zastąpiłem spacją. Oba te pliki są dostępne tutaj = ZIP A12 także po to, żebyś mógł przeprowadzić własne eksperymenty.
Być może znasz dobrze arkusze kalkulacyjne i podane dalej informacje nie są Ci potrzebne, ale większość elektroników może mieć z tym kłopoty. A tworzenie sensownych wykresów czasowych jest bardzo potrzebne, dlatego musimy temat omówić dokładniej. Pole do eksperymentów jest bardzo szerokie, a wyniki u Ciebie mogą być inne niż u mnie, zależnie od ustawień systemu operacyjnego i od ustawień programów. Możliwości są bardzo szerokie, ponieważ programy kalkulacyjne, takie jak Excel, oraz Calc z LibreOffice i OpenOffice, mają mnóstwo opcji. W sumie są to bardzo potężne i inteligentne programy. Co istotne, występują też istotne różnice między komercyjnym Microsoft Excel a darmowymi LibreOffice i OpenOffice.
I tak po otwarciu w Excelu pliku RSTRAT01literaT.csv zaznaczyłem (podświetliłem) wszystkie dane, rozszerzyłem kolumnę A, potem z menu wybrałem Wstaw – Wykresy – Liniowy, jak pokazuje rysunek 3.
Po chwili na ekranie pojawił się gotowy wykres, widoczny na rysunku 4. Zwróć uwagę, że na poziomej osi mamy od razu znaczniki czasu. Jeżeli powiększysz lub pomniejszysz wykres, liczba znaczników czasu sama się dostosuje do szerokości wykresu.
Jeżeli jednak analogicznie otworzyłem plik RSTRAT01spacja.csv, kolumna A wyglądała jak na rysunku 5.
Po rozszerzeniu kolumny A wygląd, pokazany na rysunku 6, był inny niż na rysunku 3: znikły cyfry, oznaczające liczbę sekund, a za to pojawiły się wypełniające zera i liczby są dosunięte do prawego marginesu kolumny A. Excel dokonał jakiejś operacji, a konkretnie interpretacji.
Po zaznaczeniu całości i wstawieniu wykresu liniowego, jak na rysunku 3, pojawił się bezsensowny wykres z rysunku 7.
Aby uzyskać wykres „czasowy”, w tym przypadku trzeba wybrać: Wstaw – Wykresy – Punktowy XY (ang. Scatter) według rysunku 8.
Wtedy u mnie pojawił się wykres, widoczny na rysunku 9. Prawidłowy, ale opis poziomej osi był nieczytelny.
Po kliknięciu na te nieczytelne napisy prawym klawiszem myszki wybieramy Formatuj oś (rysunek 10).
Wtedy otwiera się prawy panel, gdzie trzeba wybrać: Opcje tekstu – pole tekstowe – Kierunek tekstu, a w okienku zaznaczyć Obróć tekst o 270°, jak pokazuje rysunek 11.
Pozytywny efekt uporządkowania poziomej skali czasu jest przedstawiony na rysunku 12 (oczywiście należałoby uporządkować i poprzesuwać składniki wykresu, a także zmienić zakres osi pionowej).
W tym przypadku przy zmianie wielkości wykresu liczba znaczników czasu na osi poziomej jest stała, co raczej nie jest zaletą. A dodatkowo te opisy się powtarzają, bo jak już wcześniej zauważyliśmy, zawarta w kolumnie A informacja o sekundach została obcięta.
Okazało się, że w Excelu łatwiej zrobić potrzebny wykres z pliku, w którym data i godzina są rozdzielone literką T. Przyczyną takiego stanu rzeczy jest odmienne traktowanie danych z kolumny A przez program Excel. Przypomnijmy, że nasz program-szkic Arduino zapisuje na karcie SD nie liczby, tylko teksty, napisy. W pliku tekstowym RSTRAT01.TXT nie ma żadnych liczb. Wartości temperatury to nie liczby, tylko ciągi znaków ASCII. Przy tekstowym zapisie liczb ułamkowych stosowana jest kropka.
Ponieważ nie są to liczby, tylko napisy, nie ma żadnego problemu, gdy w Notatniku automatem zmieniamy najpierw przecinki na średniki, a potem kropki na przecinki i zapisujemy w pliku z rozszerzeniem .csv. W tym pliku .csv też nie ma liczb, tylko ciągi znaków – napisy. Gdy taki plik tekstowy zostaje wczytany do programu kalkulacyjnego, program dokonuje interpretacji. Jeśli w naszym komputerze mamy polskie ustawienia systemowe, w tym przecinek jako punkt dziesiętny w liczbach ułamkowych, to inteligentny program albo sam, bez naszej ingerencji ustala, jaki znak ASCII jest separatorem pól w rekordach (nie przecinek, tylko najczęściej średnik albo spacja), albo pyta o te szczegóły. W każdym razie przy wczytywaniu pliku tekstowego .csv na pewno następuje zamiana „napisów liczbowych” na liczby, a przy „polskich ustawieniach systemowych” komputera program traktuje przecinki jako punkty dziesiętne w liczbach i zamienia tego rodzaju „teksty liczbowe z przecinkiem” na liczby zmiennoprzecinkowe.
To jest zrozumiałe, wręcz oczywiste. Ale programy tego typu mają także mnóstwo innych możliwości. I tak istnieje możliwość, że żadna interpretacja i konwersja nie zostanie przeprowadzona. Wcześniej mówiliśmy, że w plikach .csv mogą występować napisy ujęte w cudzysłowy („”). Owszem, ale dowolny tekst bez cudzysłowów też może zostać potraktowany jako tekst. Jeżeli program napotka rekordy zawierające wyłącznie kody cyfr ASCII (i znaki dziesiętne – przecinki), na pewno domyślnie zamieni je na liczby. Ale jeśli oprócz liczb napotka tam „niezrozumiałe” znaki, to zapewne potraktuje rekord jako tekst. I właśnie Excel nie wiedział, jak potraktować rekordy, gdzie data była oddzielona od godziny literą T. Paradoksalnie właśnie to pozwoliło nam w prosty sposób uzyskać potrzebne wykresy z prawidłowo oznaczoną osią poziomą po wstawieniu wykresu liniowego.
Natomiast gdy wczytywaliśmy wersję pliku .csv, gdzie litera T została zastąpiona spacją, Excel stwierdził, że jest to standardowy zapis daty i czasu. Dlatego pierwsze rekordy każdej linii potraktował jako coś w rodzaju liczby – jako zapis daty i czasu (ściślej biorąc, data jest zapisywana w postali liczby całkowitej, ale to oddzielna sprawa). I właśnie dlatego dodał wiodące zera w jednocyfrowych miesiącach, dniach, godzinach i minutach. To nam się podoba, ale niestety obciął sekundy, bo standardowy format daty i czasu sekund nie obejmuje (jeszcze więcej jest problemów, gdyby ktoś w Excelu chciał zwizualizować, nie tylko sekundy, ale ułamki sekund – milisekundy, co jest możliwe do zapisania przy tworzeniu rekordów w rejestrowanym pliku).
Omawiane kwestie łatwiej zrozumieć, otwierając tekstowy plik RSTRAT01spacja.csv w darmowym OpenOffice albo LibreOffice. U mnie przy próbie otwarcia najpierw pokazuje się okno importu tekstu, pokazane na rysunku 13.
Gorąco zachęcam: sprawdź, jak w dolnym oknie zmienia się interpretacja tekstu przy zaznaczeniu Opcji separatora, takich jak Spacja i Przecinek. To dotyczy głównie interpretacji liczb. Potem w wersji z rysunku 13 (z jednym separatorem Średnik) kliknij na napis Standardowe w pierwszej kolumnie daty/godziny, czym zaznaczysz tę kolumnę, a następnie kliknij umieszczone wyżej pole wyboru, gdzie ukaże się kilka możliwości. Wybierz Data (RMD), czyli Rok/Miesiąc/Dzień, przez co program będzie odtąd traktował pierwszą kolumnę jako datę/czas. Poćwicz tworzenie wykresów w ten sposób, ponieważ zapewne przyda Ci się to w przyszłości!
A teraz kolejne istotne szczegóły.
Opóźnienia. Zajrzyj do naszego programu-szkicu A1201.ino i zauważ, że w głównej pętli najpierw odczytujemy czas z modułu RTC:
RtcDateTime czasAktualny = zegar.GetDateTime();
co wprowadza pewne opóźnienie z uwagi choćby na niedużą szybkość łącza I2C, a potem wydajemy „ogólne” polecenie pomiaru temperatury przez wszystkie dołączone czujniki DS18B20:
czujniki.requestTemperatures();
Przy maksymalnej rozdzielczości ten pomiar trwa do 0,75 sekundy. Wprawdzie potem też niezbyt szybko odczytujemy zmierzone wartości z kolejnych czujników, ale te opóźnienia nie zmieniają faktu, że wszystkie czujniki zmierzyły temperaturę jednocześnie. Pozostaje tylko kwestia różnicy czasu, odczytanego z RTC, a czasu, gdy tak naprawdę dokonywany był pomiar temperatury. W przypadku pomiarów temperatury nie ma to znaczenia, bo bezwładność termiczna czujników jest bardzo duża, niemniej w innych zastosowaniach takie przesunięcia czasowe mogą mieć znaczenie.
I jeszcze jedna praktyczna sprawa:
Trwałość kart SD. Otóż karty SD są rodzajem pamięci EEPROM (Flash NAND), a wobec tego też mają ograniczoną liczbę cykli zapisu. Zagadnienie jest skomplikowane. Liczba cykli zapisu, która jeszcze nie doprowadzi do degradacji właściwości i do błędów, zależy od szczegółów budowy pamięci karty. Temat jest bardzo szeroki i dość słabo znany ogółowi elektroników. Ogólnie biorąc, starsze i „mniejsze” karty pamięci mają komórki o stosunkowo dużych rozmiarach, a zawartość danej komórki to albo 0, albo 1, co odpowiada braku czy obecności ładunku elektrycznego w maleńkiej pojemności bramkowej tranzystora MOSFET (bo podstawą konstrukcji pamięci EEPROM/Flash są tranzystory MOSFET i pojemności ich bramek). Pamięci tego rodzaju są określane skrótem SLC (Single Level Cell). Ich komórki można zapisywać nawet 100 tysięcy razy, a często jeszcze więcej.
W nowszych i „większych” pamięciach po pierwsze komórki mają mniejsze rozmiary, więc mniejsze są pojemności kondensatorów pamiętających, co samo w sobie nie jest znaczącą wadą. Co gorsze i groźniejsze, dalsze zwiększenie pojemności realizuje się w nich przez rozróżnienie więcej niż dwóch poziomów naładowania kondensatora bramkowego MOSFET-a. Jeżeli rozróżnia się cztery poziomy naładowania kondensatora pamiętającego, to w jednej komórce można zapisać nie jeden, tylko dwa bity informacji. Takie „czteropoziomowe” pamięci oznaczane są skrótem MLC (Multi Level Cell). Ich komórki mogą być zapisywane 3000…30000 razy bez ryzyka błędów. Jeszcze nowsze i pojemniejsze karty pamięci rozróżniają 8 poziomów naładowania kondensatora pamiętającego, co pozwala w jednej komórce zapisać trzy bity informacji – to są pamięci niezbyt precyzyjnie oznaczane TLC (Tri Level Cell). Liczbę niezawodnych cykli zapisu komórek szacuje się na 300…3000. Kolejnym krokiem są „czterobitowe” QLC, gdzie precyzyjnie trzeba rozróżnić 16 poziomów naładowania kondensatora i których maksymalna liczba bezbłędnych cykli zapisu jest rzędu 1000 razy.
Jak widać, czym nowocześniejsza i bardziej pojemna karta, tym ma mniej cykli zapisu. Jednak nie jest to wcale takie oczywiste, zwłaszcza w kontekście rejestratorów (niewielkich ilości) danych. Problem jest szeroki i wybór karty pamięci do rejestratorów o dużej niezawodności nie jest sprawą łatwą (dotyczy zresztą nie tylko rejestratorów „danych liczbowych”, ale też popularnych rejestratorów wideo z kartami SD). Na pewno znaczenie mają szczegóły technologiczne wynikające z doświadczeń producenta kart – warto stosować karty renomowanych wytwórców.
Przy wyborze karty (micro)SD na pewno w grę wchodzi budowa – najlepsze do niezawodnych rejestratorów byłyby „zero-jedynkowe” pamięci SLC, które obecnie w postaci kart SD są niestety trudno dostępne i drogie. Dziś na rynku dominują tanie wielogigabajtowe karty MLC i TLC. A czym większa ich pojemność, tym mniejsza liczba cykli zapisu, co może budzić poważne obawy o trwałość danych. Ale ograniczona, mała liczba cykli zapisu nie musi być problemem: przecież w przypadku niewielkiej ilości zapisywanych danych, pamięci o ogromnej pojemności rzędu kilkudziesięciu gigabajtów nie sposób zapełnić nawet przez wiele lat. W podanym wcześniej przykładzie zapisywania co 10 minut 30 znaków – bajtów, co godzinę zapiszemy 180 bajtów, co dobę 4,2KB, co miesiąc 126KB, co rok tylko 1,54MB. W więc „malutka” jak na dzisiejsze standardy i niedroga karta 2GB wystarczyłaby na ponad 1000 lat takiej rejestracji.
Jednak sprawa nie jest aż tak oczywista: maksymalna liczba cykli zapisu dotyczy nie sumarycznej liczby wszystkich komórek karty, tylko liczby zapisów każdej pojedynczej komórki. Na pierwszy rzut oka wygląda na to, że w „małym” rejestratorze komórki karty o gigabajtowej pojemności będą zapisywane tylko raz. Niestety, tak nie jest, ponieważ w kartach (micro)SD komórki nie są zapisywane indywidualnie, tylko grupami – blokami, sektorami o określonej, często zaskakująco dużej wielkości. A karta SD to nie tylko pamięć FLASH: jej pracą i zapisem danych steruje wbudowany w kartę SD dość silny mikrokontroler i we wcześniej używanej, częściowo zapisanej karcie, przed cyklem zapisu nawet jednej komórki, wykonywany jest cykl kasowania całego dużego bloku/sektora, a potem zapisywane są razem nie tylko nowe wartości, ale też „stare wartości” wcześniej przepisane do innych komórek tego bloku. Kontroler odpowiedzialny jest także za funkcje, takie jak balansowanie zużycia karty (wear leveling – równoważenie zużycia), wychwytywanie i korekcję błędów, itp. Szczegóły są złożone i zależne od rodzaju, pojemności pamięci oraz algorytmów stosowanych przez danego producenta. Jeśli chcesz – samodzielnie poszukaj dalszych informacji.
W każdym razie jeżeli kluczową sprawą jest trwałość nośnika danych, należałoby tak zorganizować zapis w rejestratorze, żeby po pierwsze minimalizować liczbę cykli kasowania bloków pamięci (choćby przez wykorzystanie zupełnie nowej, nieużywanej karty), a po drugie, trzeba minimalizować liczbę cykli zapisu przez zwiększenie wielkości zapisywanych porcji, np. zapis kilku, kilkudziesięciu czy nawet tysięcy rekordów wcześniej przechowywanych w jakiejś, najlepiej nieulotnej, pamięci pomocniczej.
Są to zaawansowane zagadnienia, wymagające szerszej wiedzy. Do rejestratorów profesjonalnych należy stosować dużo droższe karty (micro)SD (przemysłowe, a nie konsumenckie), przeznaczone do tego właśnie celu W prostych amatorskich układach Arduino nie mamy znaczącego wpływu na te sprawy i kwestią trwałości karty SD nie trzeba się zbytnio przejmować. Często najprostszym sposobem zwiększenia niezawodności jest zastosowanie dwóch rejestratorów.
W następnym artykule (UR013) cyklu nadal będziemy zajmować się rejestracją danych.
Piotr Górecki