logo elektroda
logo elektroda
X
logo elektroda
REKLAMA
REKLAMA
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

Wemos D1 ESP8266+DHT11 - stacja pogody z wykresami część 2 - zapis w pamięci Flash

p.kaczmarek2 06 Cze 2024 11:39 1644 2
REKLAMA
  • Wykres temperatury i wilgotności na stacji pogodowej ESP8266
    Zapraszam na drugą część przygody z płytką Wemos D1 ESP8266 i czujnikiem temperatury/wilgotności DHT11. W tej części rozwinę swój program o zapis wyników w pamięci Flash ESP8226, wykorzystam do tego bibliotekę o wdzięcznej nazwie EEPROM. Dlaczego klasa do zapisu we Flash nazywa się tutaj EEPROM? Przekonajmy się!

    Poprzedni temat z serii:
    Wemos D1 "Arduino" i DHT11 - prosta stacja pogody z wykresami na stronie WWW

    Zaraz zaczynamy, ale najpierw...
    Przestroga przed zużyciem pamięci Flash
    W tym temacie prezentuję zapis wyników w pamięci Flash. Jest to proste i dostępne, więc jak najbardziej kuszące, ale może być też zwodnicze. Pamięć flash się dość szybko zużywa na skutek wykonywanych cyklów kasowania.
    Załóżmy, że nasz moduł z ESP8266 ma na pokładzie pamięć W25Q32, jest to pamięć podłączona do niego przez SPI, w tej pamięci przechowywany jest nasz program, ale możemy też zapisywać tam pomiary.... jednakże ta pamięć ma pewne ograniczenia. W nocie katalogowej mówi o nich jedna linijka, zaszyta w wielu innych informacjach:
    [Fragment noty katalogowej pamięci Flash W25Q32JV z zaznaczoną informacją o 100 tysiącach cykli programowania/kasowania na sektor]
    100 000 cykli kasowania na sektor... zakładając najgorszy scenariusz, że zapisujemy pomiary do jednego sektoru, oraz zakładając że każdy zapis poprzedzony jest kasowaniem, to mamy 100 000 zdarzeń zapisu pomiarów.
    100 000 pomiarów, ile to jest? Załóżmy jeden pomiar na godzinę.
    100000/24 to około 4166 dni. Czyli około 11 lat...
    Nie jest to raczej zbyt dobry wynik jeśli chcemy stworzyć coś więcej niż prosty program do zabawy. A przy np. 10-krotnie większej częstotliwości zapisów już spadamy do 1 roku... (minimum).
    Jednakże ten problem rozwiążę w trzeciej części, na razie spróbujmy to pominąć i po prostu poznać najprostszy sposób zapisu do Flash.

    Zaczynamy. W ramach przypomnienia zamieszczam kod z poprzedniego tematu:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod



    Pamięć "EEPROM"
    ESP8266 nie posiada pamięci EEPROM, ale ktoś pomysłu wpadł na pomysł emulacji jej poprzez pamięć Flash. Na początek możemy spróbować tego użyć. Nie jest to zbyt dobry pomysł, bo częsty zapis będzie nam zużywać cenne cykle erase flash, więc musimy unikać zbyt częstego zapisu, ale w ramach ćwiczeń i tak można spróbować to zrobić.
    Zatem załączamy nagłówek:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Jakby co, cała dokumentacja jest tutaj:
    https://arduino-esp8266.readthedocs.io/en/latest/libraries.html
    W dokumentacji też jest przestroga przed szybkim zużyciem flash:
    Cytat:

    Note that the sector needs to be re-flashed every time the changed EEPROM data needs to be saved, thus will wear out the flash memory very quickly even if small amounts of data are written. Consider using one of the EEPROM libraries mentioned down below.

    Warto też zobaczyć kod źródłowy użytej biblioteki:
    https://github.com/esp8266/Arduino/blob/master/libraries/EEPROM/EEPROM.cpp
    Do zapisu i odczytu mamy szablonowe funkcje put i get, które robią wszystko za nas. Dodatkowo trzeba uruchomić EEPROM funkcją begin:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    W miejsce x wstawiamy rozmiar EEPROM do użycia.
    EEPROM może mieć tu rozmiar do 4096, zatem trzeba policzyć ile co zajmie miejsca. Nasza struktura to, przypominam:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Ma to u nas 16 bajtów, zresztą sprawdźmy, sizeof prawdę nam powie:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    16 bajtów:
    Zrzut ekranu z konsoli pokazujący wyniki pomiarów temperatury i wilgotności z DHT11.
    Skoro tak, to w 4096 zmieści się ta struktura całe 256 razy, ale chcemy jeszcze móc zapisać indeks ostatniego pomiaru, więc załóżmy, że zapisywać będziemy 255 próbek:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Zatem implementujemy, najpierw - odczyt:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Na pozycji 0 w pamięci będzie integer - indeks ostatniej próbki z bufora kołowego, a potem za nim będą kolejno próbki.
    Teraz zapis:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Zapis wymaga jeszcze wywołania commit aby dokonać właściwego zapisu zmian.
    Trzeba jeszcze sprawdzić czy to działa. W tym celu dałem dodatkowo wyświetlanie do konsoli indeksu ostatniej próbki:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Wgrywamy i obserwujemy zmiany w konsoli:
    Zrzut ekranu z konsoli pokazujący wyniki pomiarów temperatury i wilgotności oraz rozmiar struktury danych.
    Czy po restarcie płytki odliczanie próbek wznowi się od zapisanego indeksu?
    Zrzut ekranu z konsoli Arduino pokazujący połączenie z Wi-Fi i odczyty temperatury oraz wilgotności.
    Tak, wygląda na to, że zapis działa.
    Ale chwila...
    Wykres się nie wyświetla!
    W konsoli mamy błąd:
    Komunikat błędu
    Wykonujemy "Zbadaj źródło" na stronie by zobaczyć co poszło nie tak:
    Zrzut ekranu błędu JavaScript na stronie z wykresem wartości
    Mamy w pamięci mnóstwo wartości nan, czyli "not a number". Trzeba to naprawić.


    Ochrona przed śmieciami w pamięci
    Nasz problem wynika stąd, że bieżąca wersja kodu naiwnie wczytuje z pamięci to co tam jest, bez jakiegokolwiek weryfikowania tych danych. A przecież ten system dopiero co opracowaliśmy i nie wiemy co w tej pamięci jest, wartości tam mogą być całkiem niezdefiniowane.
    Można by po prostu ręcznie wyczyścić tą pamięć raz i ustawić tam same zera, ale można też poradzić sobie nieco sprytniej. Wystarczy użyć sumy kontrolnej.
    Suma kontrolna to specjalna wartość która jest obliczana na podstawie większej ilości danych z pamięci. Funkcja sumy kontrolnej zaprojektowana jest tak, by mała zmiana danych skutkowała od razu zmianą wyniku tej sumy. A więc jeśli policzymy sumę kontrolną dla danej tablicy i przerzucimy chociażby jeden bit w tej tablicy, to suma kontrolna powinna się już zmienić. Oczywiście nie jest to idealne rozwiązanie, bo istnieją tzw. kolizje, ale przy tym zastosowaniu pozwolę to sobie pominąć...
    Popularnym systemem sum kontrolnych jest CRC32. W sam raz na 4 bajty, 32-bity, mamy też do niego gotową bibliotekę:
    Zrzut ekranu przedstawiający bibliotekę CRC32 w Arduino
    Przykład od autora biblioteki:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    My musimy liczyć sumę kontrolną całego bloku danych przy każdym zapisie oraz zapisywać ją wtedy do EEPROM:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Dla uproszczenie nie liczę sumy kontrolnej też dla indeksu ostatniej próbki, ale generalnie to też powinienem robić.
    Od teraz moja struktura pamięci jest następująca:
    - offset 0 - ostatnia próbka (integer, 4 bajty)
    - offset 4 - suma kontrolna crc32 (integer, 4 bajty)
    - offset 8 - dane pomiarów
    W momencie startu programu, przy odczycie próbek, musimy odczytywać "starą" sumę kontrolną i liczyć to samo dla wczytanych danych. Potem porównujemy te wartości i jeśli są takie same, to zakładamy, że nie nastąpiła żadna niepożądana zmiana. Jeśli są różne, to na pewno coś poszło nie tak bądź w pamięci nie było żadnych próbek:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Jeśli już wiemy, że w pamięci nie ma poprawnych danych, to musimy też tę pamięć ręcznie wyczyścić, policzyć poprawną sumę kontrolną i zapisać do niej te dane:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Pora przetestować opracowany mechanizm. Na ten moment w pamięci są nieoczekiwane wartości, więc spodziewać się będziemy najpierw raz komunikatu o złej sumie kontrolnej, a potem z każdym rebootem płytki ta suma powinna być już w porządku.
    Konsola z wynikami zapisu i odczytu danych w projekcie ESP8266
    Po wciśnięciu RESET:
    Zrzut ekranu z serial monitora Arduino, pokazujący połączenie z Wi-Fi, odczyty temperatury i wilgotności.
    Rzeczywiście, za drugim odczytem suma kontrolna była już poprawna. System działa:
    Wykres temperatury i wilgotności na stacji pogodowej ESP8266
    Wartości pomiarów są odpowiednio pamiętane, nawet po utracie zasilania urządzenia.

    Podsumowanie
    W tej części poznaliśmy klasę EEPROM która w tym wydaniu zapewnia dostęp do... pamięci Flash oraz zorganizowaliśmy działający, ale niezbyt wydajny zapis danych do tej klasy. Przy okazji poćwiczyliśmy tematy takie jak suma kontrolna, itd.
    Opracowany system działa, ale nie nadaje się do częstego zapisu wyników. Jeszcze gdybyśmy korzystali z EEPROM do zapisu jakiś ustawień (np. hasła WiFi, tylko gdy zmienia je użytkownik) to byłoby lepiej, ale do częstych zapisów to się nie nadaje.
    Z tego też powodu w trzeciej części przepiszę ten program i zoptymalizuję go pod kątem redukcji zużycia pamięci Flash. Zobaczymy (i policzymy) ilukrotnie uda mi się wydłużyć szacowany czas życia naszych biednych sektorów.
    Oczywiście, można by też po prostu użyć zewnętrznej pamięci EEPROM... kod byłoby łatwo na to przerzucić. Zobaczmy do noty katalogowej jakiegoś EEPROM ile on wytrzyma cykli. Przykładowo 24LC256:
    Charakterystyka pamięci EEPROM 24LC256
    Jak na razie - to tyle. Zapraszam do podzielenia się własnymi doświadczeniami z omawianą tematyką, jak byście Wy zorganizowali przechowywanie tych pomiarów?

    Fajne? Ranking DIY
    Pomogłem? Kup mi kawę.
    O autorze
    p.kaczmarek2
    Moderator Smart Home
    Offline 
  • REKLAMA
  • #2 21109511
    khoam
    Poziom 42  
    p.kaczmarek2 napisał:
    Dlaczego klasa do zapisu we Flash nazywa się tutaj EEPROM?

    Klasa EEPROM jest nie rozwijana od ponad 2 lat. W jej miejsce jest natomiast dostępna klasa LittleFS i ją powinno się stosować do nowych projektów.
  • #3 21109534
    p.kaczmarek2
    Moderator Smart Home
    Słuszna uwaga, LittleFS też warto omówić. Będzie uwzględnione.
    Pomogłem? Kup mi kawę.
REKLAMA