Powrót

Kurs Arduino – rejestrator z modułem OpenLog

W trzynastym odcinku kursu Arduino zrealizujemy nową wersję rejestratora – loggera z wykorzystaniem modułu OpenLog. Ujawni się też ciemna strona Arduino – nieoptymalny, zdecydowanie zbyt rozwlekły kod programu.

W poprzednim artykule (UR012) nareszcie zrealizowaliśmy rejestrator (logger) zapisujący i wyniki pomiarów, i czas ich wykonania. Ćwiczyliśmy też sposoby graficznego zobrazowania danych na wykresach. Rejestracja danych to ważne zagadnienie, dlatego nadal drążymy ten temat. Ale od innej strony.

Na początek wracamy do programu – szkicu z poprzedniego odcinka (A1201.ino).

Rysunek 1

I tak rysunek 1 wskazuje, że po jego skompilowaniu, pamięć programu wypełniona jest w 2/3 (66%), podobnie pamięć RAM (64%). Nie jest to tragedia, ale niewątpliwie jest to przejaw ciemniejszej strony Arduino. Mianowicie niektóre biblioteki powodują, że program się rozrasta i nierzadko uniemożliwia to stworzenie bardziej rozbudowanych programów. Duża objętość kodu nie świadczy automatycznie, że biblioteka jest niedopracowana. Być może właśnie dopracowanie szczegółów oraz uwzględnienie różnych dodatkowych czynników i opcji powoduje wzrost objętości kodu. Ale często darmowe biblioteki okazują się niedopracowane. Rozstrzygnięcie kwestii jakości biblioteki nie jest takie łatwe. W wielu przypadkach hobbysta, który napotka problem braku pamięci, szuka po prostu innych, pokrewnych bibliotek i sprawdza, która daje kod wynikowy o mniejszej objętości. My ten aspekt zagadnienia pominiemy, natomiast spróbujemy odciążyć Arduino w inny sposób.

W tym celu, zamiast modułu obsługującego karty microSD za pomocą magistrali SPI, wykorzystamy specjalizowany moduł rejestratora.

Fotografia 2 i rysunek 3 pokazują wygląd i schemat modułu OpenLog, opracowanego przez Natana Seidle’a ze znanej „arduinowej” firmy SparkFun (SparkFun i Adafruit to dwie wiodące firmy, oferujące moduły i darmowe oprogramowanie do systemu Arduino).

Fotografia 2

Rysunek 3

Właśnie na stronie sparkFun:

https://learn.sparkfun.com/tutorials/openlog-hookup-guide/all

w skrócie: https://bit.ly/2D9PGPy

można znaleźć obszerny opis i wskazówki wykorzystania.

Wcześniej moduły OpenLog były dość drogie. Oryginały kosztują kilkanaście dolarów (w kraju nawet ponad 70 złotych), natomiast chińskie klony można dziś kupić za mniej niż 3 dolary.

Jak pokazuje rysunek 3, rejestrator OpenLog zawiera procesor ATmega328P, czyli taki sam, jak Arduino UNO oraz gniazdo kart microSD (zieloy blok). Karty nie mogą być zasilane napięciem 5V, więc na płytce jest też stabilizator LDO MIC5206 3,3V, co pozwala zasilać moduł dowolnym napięciem w zakresie 3,3V do teoretycznie nawet 16V (zalecane 3,3…5V).

Głównym złączem modułu jest JP2 (kolor żółty). W sumie mamy pięć pinów: masę, zasilanie (RAW) oraz piny łącza szeregowego RXI, TXO, DTR, gdzie DTR to wejście zerowania mikrokontrolera modułu.

Działanie jest beznadziejnie proste: podczas normalnej pracy – rejestracji moduł OpenLog czeka na informacje przesyłane na wejście szeregowe RXI. Gdy pojawią się tam informacje, a konkretnie znaki ASCII, odbiera je i zapisuje na karcie microSD.

Moduł ma też dodatkowe złącze SPI, a właściwie tylko piny (SPI header). Nie jest ono wykorzystywane podczas normalnej pracy; w razie potrzeby może posłużyć do wgrania do procesora modułu zaktualizowanego/zmodyfikowanego oprogramowania.

Wnikliwi Czytelnicy być może zauważą tu pewien problem sprzętowy. Otóż i czytnik kart microSD, i procesor ATmega328P modułu OpenLog są zasilane napięciem 3,3V. Procesor ten ma na wejściach typowe obwody ochronne, zawierające diody włączone między wejście a obie szyny zasilania. Jak pokazuje rysunek 3, między pinem  wejściowym RXI złącza JP2 a nóżką 30 procesora (RXD) nie ma żadnego translatora poziomów, czy choćby ogranicznika prądu w postaci szeregowego rezystora.

To wskazywałoby, że moduł OpenLog może współpracować tylko z systemami zasilanymi napięciem 3,3V. Tymczasem moduły te często współpracują z urządzeniami zasilanymi napięciem 5V. W dokumentacji modułu na stronie: https://github.com/sparkfun/OpenLog/wiki/datasheet

w skrócie https://bit.ly/2SyK8aW

można przeczytać: ###Power### Input voltage on VCC can be 3.3 to 12V. Input voltage on RXI pin must not exceed 6V. Output voltage on TXO pin will not be greater than 3.3V;

z czego wynika, że napięcie wejścia szeregowego RXI nie może przekraczać 6V.

Jednak dobry elektronik w finalnym układzie, na wejściu RXI, włączyłby w szereg dodatkowy rezystor (220Ω…1kΩ). My w ramach kursu Arduino pomijamy wiele szczegółów, więc i teraz, wzorem wielu innych przykładów z sieci, możemy pozwolić sobie na bezpośrednie połączenie naszego Arduino Uno z OpenLog.

Nasz układ testowy może mieć schemat jak na rysunku 4.

Rysunek 4

Ja z wcześniejszego systemu usunąłem moduł kart microSD, a w zamian dołączyłem moduł OpenLog, i to tylko trzema przewodami. Nie wykorzystujemy tu ani wejścia resetującego DTR, ani wyjścia danych TXO.

Z pewnych względów nie chciałem lutować modułu OpenLog, więc dołączyłem go za pomocą miniaturowych krokodylków, co jest pokazane na fotografii 5.

Fotografia 5

Moduł OpenLog wykorzystuje łącze szeregowe i rejestruje na karcie microSD wszystko, co pojawi się na jego wejściu RXI. W naszym Arduino Uno mamy łącze szeregowe, gdzie jest ono dostępne na pinach 0, 1. Tak, ale łącze to wykorzystywane jest do programowania, bo przecież mamy stałe szeregowe połączenie z komputerem (za pośrednictwem łącza i kabla USB). Gdybyśmy więc dołączyli OpenLog do sprzętowego łącza, na karcie zapisywane byłoby wszystko, co jest przesyłane z Arduino Uno do komputera. Właśnie dlatego dołączamy wejście RXI OpenLog do innego pinu Arduino, gdzie zrealizujemy programowy port szeregowy.

Jeżeli chodzi o program, to znów bez głębszego zastanowienia chcemy wykorzystać i zmodyfikować jakiegoś gotowca. We wcześniejszych ćwiczeniach zaczynaliśmy od znalezienia odpowiedniej biblioteki, do której dołączone są gotowce. Jeżeli zrobimy podobną operację, to w menedżerze bibliotek znajdziemy jedną tylko propozycję SparkFun Qwiic OpenLog, co pokazuje rysunek 6.

Rysunek 6

Instalowanie tej biblioteki byłoby błędem, ponieważ jest ona potrzebna tylko dla zmodyfikowanej wersji OpenLog. Mianowicie SparkFun wypuścił szereg różnych modułów ze złączem I2C (IIC). Seria ta ma w oznaczeniu skrót Qwiic, a płytki zawierają dwa gniazda łacza I2C.

Fotografia 7

Fotografia 7 przedstawia obie strony wersji Qwiic OpenLog, gdzie nie ma łącza szeregowego, tylko I2C (TWI). Biblioteka z rysunku 6 jest więc zupełnie niepotrzebna!

Tylko skąd wziąć gotowca?

Zapewne pomyślisz: GitHub!

Jak najbardziej, pod adresem https://github.com/sparkfun/OpenLog

znajdziemy wiele materiałów. Ale nie bibliotekę OpenLog dla Arduino, ponieważ… nie jest potrzebna. Owszem, w zakładce Libraries znajdziemy biblioteki, ale są to zmodyfikowane wersje SdFat i SerialPort, które nie są niezbędne, a zajęcie się nimi doprowadziłoby początkujących do niemałej frustracji.

Na początek wystarczy zawarta w Arduino IDE biblioteka SoftwareSerial, którą już zresztą wykorzystywaliśmy przy okazji testów czujnika dwutlenku węgla.

A jeżeli chodzi o gotowce, nie szukaj ich w zakładce github.com/sparkfun/OpenLog/Libraries, tylko w zakładce OpenLog/firmware, gdzie znajdziesz katalog /Arduino_Examples. Wewnątrz jest szereg szkiców .ino, a nas interesuje pierwszy:

/Example1_SoftwareLogging.ino.

Jeśli go otworzysz i przeanalizujesz, zapewne będziesz zaskoczony. Jego uproszczona wersja, pokazująca jednak w pełni istotę wykorzystania OpenLog, widoczna jest w szkicu 1 (A1301.ino). Szkic jest beznadziejnie prosty:

 
#include <SoftwareSerial.h>
SoftwareSerial OpenLog(2, 3);
 
void setup() {
 OpenLog.begin(9600);
 OpenLog.println(„Co u Ciebie?”);
 OpenLog.println(„Jakis napisik”);
 OpenLog.println(„trzecia linijka”);
 OpenLog.println(„koniec testu”);
}
void loop() { } //pusta pętla

Na początku dołączamy wbudowaną w Arduino IDE bibliotekę SoftwareSerial, potem na jej podstawie konstruujemy obiekt, który nazywamy jak chcemy, na przykład OpenLog, który zrealizuje i obsłuży programowy port szeregowy na pinach 2 (wejście – RXD, którego nie wykorzystujemy) i 3 (wyjście – TXD). Pin 3 Arduino, czyli programowe wyjście TXD, łączymy z wejściem RXI OpenLog (rysunek 4). W szkicu tym, w jednorazowym programie setup() najpierw dokonujemy inicjalizacji, otwieramy nasz programowy port szeregowy (który nazwaliśmy OpenLog), a potem za jego pomocą do modułu OpenLog wysyłamy cztery krótkie linijki tekstu. OpenLog powinien je zapisać na karcie microSD.

Ja włożyłem do modułu OpenLog pustą, wyczyszczoną kartę microSD, a po skompilowaniu i uruchomieniu szkicu A1301.ino na karcie pojawiły się dwa pliki tekstowe, pokazane na rysunku 8.

Rysunek 8

Pliki te stworzył program, zawarty w module OpenLog. Zgodnie z oczekiwaniami zawartość pliku LOG00000.TXT była taka, jak widać na rysunku 9.

Rysunek 9

Sukces! Wykorzystanie modułu rejestrującego OpenLog okazało się beznadziejnie łatwe!

W następnym kroku spróbujmy z użyciem modułu OpenLog zrealizować pełnowartościowy rejestrator, modyfikując program A1201.ino z poprzedniego ćwiczenia.  Aby ocenić, ile pamięci pożerają poszczególne zadania, ja najpierw wyczyściłem ten program z instrukcji związanych z modułem kart pamięci. Ta (nierejestrująca) wersja, zawarta w dostępnym w Elportalu szkicu A1302.ino, po skompilowaniu zajmuje 39% pamięci programu i 28% pamięci RAM, jak pokazuje rysunek 10.

Rysunek 10

Teraz trzeba połączyć szkic A1302.ino z króciutkim A1301.ino ze szkicu 1. W efekcie powstał szkic A1303.ino.

Rysunek 11

Jak widać na rysunku 11, po skompilowaniu szkic ten zajmie 43% pamięci programu i 34% pamięci RAM. Porównaj z tym rysunek 1 pokazujący, że wcześniejszy program A1201.ino obsługujący kartę SD za pomocą łącza SPI zajmował odpowiednio 66% pamięci programu i 64% pamięci RAM.

Jest to bardzo duża różnica, którą uzyskaliśmy dzięki użyciu modułu OpenLog. Procedury obsługi karty (micro)SD zawarte są teraz w procesorze modułu OpenLog. My tylko musieliśmy za pomocą łącza szeregowego przesłać do modułu interesujące nas dane do zapisania.

Aby pokazać, że program A1303.ino działa, zarejestrowałem krótką sekwencję pomiaru temperatury za pomocą trzech czujników. Jeśli zestawisz taki układ, zauważysz, że króciutko migają dwie diody w module OpenLog: jedna, gdy pojawiają się dane na łączu szeregowym i druga, gdy następuje zapis danych na kartę SD.

Na mojej karcie wcześniej był już plik LOG00000.TXT. OpenLog inteligentnie założył i zapisał nowy plik o kolejnym numerze porządkowym: LOG00001.TXT (u Ciebie, zależnie od kolejności działań i programu wpisanego do ArduinoUno, na początku pliku mogą się znaleźć cztery linijki tekstu ze szkicu A1301.ino, które można skasować).

Po dokładnie opisanych w poprzednim odcinku zamianach (w Notatniku) przecinków na średniki i kropek na przecinki, zapisałem plik LOG00001.csv i ten plik wczytałem do darmowego OpenOffice. Potem ze zgromadzonych danych szybciutko zrobiłem prowizoryczny wykres (punktowy XY) widoczny na rysunku 12.

Rysunek 12

Moduły OpenLog mogą się okazać znakomitą pomocą w realizacji różnych urządzeń. Już wiemy, że pozwalają znacząco odchudzić program w Ardiuno. Ale jest i inna zaleta. Ostatnio mówiliśmy, że karty SD mają ograniczoną liczbę cykli zapisu i najlepiej byłoby zapisywać na karcie jak największe porcje danych. Wprawdzie OpenLog niewiele tu pomoże, bo przy seryjnym zapisie może wprawdzie zapisywać nieco większe porcje, ale ograniczeniem jest pamięć RAM jego procesora. Rejestracja polega na tym, że OpenLog nasłuchuje i czeka na dane (znaki ASCII) z wejścia RXI. Nadchodzące dane przepisuje do 512-bajtowego bufora i zawartość napełnionego bufora zapisuje na karcie (o ile nie nastąpi dłuższa, sekundowa przerwa transmisji). Z tego względu bufor wejściowy portu szeregowego OpenLog powinien być jak największy. Problem w tym, że ATmega328P ma 2 kilobajty pamięci RAM, więc na bufor odbiorczy można przeznaczyć mniej niż te 2KB (standardowo ma on 512 bajtów).

Jak jednak mówiliśmy, sama karta SD i jej kontroler mają mechanizmy zapobiegające szybkiemu zużyciu (WearOut protection). Niemniej problem niezawodności zapisu na kartach SD jest aktualny. I właśnie tanie moduły OpenLog pozwalają obejść problem i radykalnie zwiększyć niezwodność rejestracji, a to przez zastosowanie dwóch lub więcej modułów OpenLog, połączonych niejako równolegle.

Na koniec króciutko o jeszcze jednej ważnej sprawie. Gdy włożyliśmy do OpenLog czystą kartę microSD, automatycznie został na niej zapisany plik config.txt (patrz rysunek 8). Jest to plik konfigurujący działanie modułu OpenLog. Jego zawartość pokazana jest na rysunku 13. Określa on sześć istotnych parametrów/ustawień modułu.

Rysunek 13

My w ćwiczeniach wykorzystaliśmy gotowca i nie zastanawialiśmy się nad szczegółami, takimi na przykład jak szybkość transmisji.

W pliku konfiguracyjnym najważniejsza jest pierwsza linia. Druga linia to tylko pomoc – przypomnienie i ściągawka. Łatwiej zrozumieć sens zapisu po przedstawieniu treści pliku w sposób pokazany na rysunku 14.

Rysunek 14

Pierwsza liczba to prędkość transmisji, domyślne 9600, ale może wynosić 2400, 4800, 9600, 19200, 38400, 57600, 115200 bps.

My wykorzystywaliśmy OpenLog w najprostszy sposób: moduł jedynie rejestrował dane przychodzące na wejście RXI. Oprócz trybu rejestracji, moduł może też pracować w trybie rozkazowym (command mode). Istnieją dwa sposoby wprowadzenia modułu w ten drugi tryb. W pierwszym wykorzystuje się fakt, że gdy moduł OpenLog zostaje zasilony, odczytuje zawartość pliku config.txt z karty (o ile taki plik tam jest, bo jeśli nie ma, to taki plik zostanie tam zapisany). I jeśli w odczytanym z karty pliku config.txt na czwartej pozycji (mode – tryb) znajdzie jedynkę, wtedy wchodzi w tryb rozkazowy. Domyślnie na czwartej pozycji jest 0, co daje tryb rejestracji.

Drugi sposób wejścia w tryb rejestracji polega na tym, że gdy moduł jest w trybie rejestracji, na wejście RXI wysłany zostaje specjalny kod-klucz. Jak wiemy, moduł czeka na znaki – kody ASCII i je zapisuje na karcie. Można wybrać znak ASCII, który będzie służył jako kod-klucz, ale musi to być  jakiś kod nieużywany podczas normalnego zapisu. Autor OpenLog ustalił, że domyślnie przejście z trybu rejestracji do trybu rozkazowego następuje po wysłaniu na wejście RXI modułu trzech znaków $, które w kodzie ASCII mają numer 36. I właśnie to określają pozycje druga (36) i trzecia (3) w pliku config.txt. Można to zmienić.

Pozycja piąta (1) określa reakcję na błędy: wartość 1 to rozszerzona reakcja na błędy, 0 to reakcja uproszczona.  Opcja ta dotyczy trybu rozkazowego.

Szósta pozycja (1) określa, że w trybie rozkazowym komendy wysyłane do modułu są też od razu odsyłane (jak echo) przez wyjście TXO.

Ostatnia pozycja ma wartość zero, to znaczy, że NIE ignoruje zerowania od wejścia RXI. Jeżeli dostaniemy używany i „poprzestawiany” moduł OpenLog, to można go „wyzerować” do ustawień domyślnych przez dołączenie wejścia RXI do masy i włączenie zasilania. Czasem trzeba to zablokować/wyłączyć, by np. podczas pracy nie nastąpiło „wyzerowanie” zmienionych ustawień i wtedy na ostatniej pozycji powinna znaleźć się jedynka.

Dalsze informacje o trybie rozkazowym podane są w następnym artykule (UR014), w którym nadal zajmujemy się rejestracją danych.

Piotr Górecki