sobota, 10 grudnia 2016

Własne klocki LEGO - pierwsze prywatne biblioteki

Każdy śpiewać może trochę lepiej lub trochę gorzej...


Podzieliłem byłem swój program BRAMA na dwie części (opis tutaj) pomiędzy ESP i NANO. To niebywałe jak taki niewinny zabieg ułatwia "panowanie" nad tworzonym kodem.
Stworzenie wąskiego gardła jakim jest serial do wymiany danych pomiędzy modułami znakomicie porządkuje sprawę zmiennych choć na pierwszy rzut oka powinno to utrudniać a nie ułatwiać pisanie nowego kodu. Ale  takie ograniczenie w sposób naturalny zmusza do porządku i dyscypliny w zawiadywaniu zmiennymi. Ogranicza swobodę (a wręcz swawolę) jaką mamy wewnątrz programu kiedy chcemy przesłać informację z jednego segmentu do drugiego. Mogę to zrobić przecież na 1000 i jeden sposobów. I robię. Przez to mapa wektorów przesyłanych informacji pomiędzy programami/funkcjami ww programie (gdyby ją stworzyć) wyglądałaby pewnie jak kłębowisko poskręcanych kabli pod moim stołem. Zapanowanie nad tym jest możliwe do pewnego momentu. Potem trzeba z mozołem śledzić drogę przesyłu takich sygnałów i co one tam po drodze robią lub powinny robić. To konieczność wielokrotnego przeskanowywania całego kodu programu by prześledzić całą ścieżkę od początku do końca i to bez żadnej gwarancji że czegoś tam po drodze nie przeoczyłem.

Stworzona sztucznie "wąska rura" przesyłu danych to rodzaj wiązki z ładnie poukładanymi kablami łączącego pomiędzy sobą różne urządzenia. Nie trzeba już podążać za każdym pojedynczym sygnałem - wystarczy popatrzeć jakie bloki łączy stworzony kanał i jaki rodzaj sygnałów mamy w wiązce. Jeśli jeszcze w jakiś logiczny sposób ponazywamy te sygnały to ich obróbka po obu stronach wiązki znakomicie się upraszcza. Jedyny problem to wybór "szerokości" kanału przesyłowego tak by w przyszłości nie trzeba było pruć całej wiązki by dołożyć jeszcze jeden potrzebny kabel.
Sądzę że właśnie taką funkcję spełniają w programie STRUKTURY i trzeba będzie się nimi zająć na poważnie. Najpierw jednak trzeba nauczyć się dzielić program na niezależne bloki i umieć je wzajemnie skomunikować.

Na pierwszy ognień poszedł kod BRAMY3 nazwany dla niepoznaki BRAMA 3 1/2. To kod oparty na podziale zadań pomiędzy NANO i ESP-01. NANO odpowiada za sterowanie peryferiami a ESP za komunikację z BLYNK. Połączenie obu mikrokontrolerów dokonuje się za pomocą sprzętowego seriala na obu modułach. Robi to wszytko co poprzednie BRAMY:
- symuluje pilota bramy garażowej i sygnalizuje stan otwarcia bramy
- symuluje wszystkie piloty oświetlenia w domu i na zewnątrz
- steruje pompą obiegu CO
- steruje kotłem CO równolegle do bezprzewodowego termostatu
- mierzy temperaturę
- miga ledami i ma inne nieistotne bajery
Kod jest mocno rozwojowy w tej strukturze więc pewnie będziemy do niego wracać jeszcze nie jeden raz.
Po podziale kodu na dwa osobne procesory postanowiłem dodatkowo poszatkować kody na funkcjonalne biblioteki.To taka zabawa/wprawka ale i test czy stosowanie własnych bibliotek w czymkolwiek pomaga przy tak niewielkich (400-600 linii) kodach.

Część zainstalowana w NANO składa się z części głównej kodu zapisanej na chwilę obecną jako brama3v7.ino i wydzielonych bibliotek  "klaw.h", "transmit.h" "dallas.h"  "pompa.h" "piec_co.h"  Nazwy wskazują funkcje programu jakie zostały w nich skupione.
Wszystkie biblioteki znajdują się w podkatalogu głównego programu brama3v7.ino dlatego wywołuje się je poprzez #include "klaw.h" w nawiasach "" a nie w < >.


Program główny brama3v7.ino


W programie głównym są umieszczone podstawowe pętle programu SETUP ( ze wszystkimi ustawieniami początkowymi programu) i LOOP (zminimalizowana do trzech funkcji - obsługi w poolingu timera, odbioru z 433 MHZ i odbioru z RS232).  Zostawiłem też tutaj programy błyskania ledami kontrolnymi na płycie głównej i w aplikacji BLYNK, czytanie stanu przełącznika, testowanie komunikacji z ESP i jego ewentualne resetowanie.

Oznacza to, że NANO pozostało wiodącym procesorem w systemie - kierującym i nadzorującym w całości jego poprawne funkcjonowanie.Pytanie kto nadzoruje NANO jest jak najbardziej uzasadnione. Jeśli NANO padnie lub się zawiesi - na dziś  niestety - powiesi się cały system. Ale to jest tylko stan przejściowy. W następnych ćwiczeniach praktycznych NANO zostanie przeformatowane poprzez wgranie nowego bootloadera tak by uruchomić można było na nim sprzętowy watchdog zawarty w ATMEGA328. Na dziś ze względu na błąd w bootloaderze watchdog wiesza cały procesor. Dodam też być może krosowe sprawdzanie NANO przez ESP i powiadamianie o awarii do aplikacji BLYNK. Ale to przyszłość.

Na razie w programie głównym pozostawiłem wszystkie wywołania procedur uruchamianych zmianą stanów vPinów w BLYNK (66-110) by mieć to wszystko w jednym miejscu i łatwo kontrolować modyfikacje. Chwilowo jest tu też sterowanie i kontrola otwierania bramy (130-155) ale i ona pewnikiem trafi do swojej biblioteki.

Na początku programu głównego zadeklarowane są moje biblioteki "" ale także niektóre biblioteki systemowe < > znajdujące się w katalogu "libraries" użyte w moich bibliotekach. Nie wiem czemu ale musiałem powtórzyć te deklaracje także tutaj by kompilator nie wyrzucał błędów. Dobrze, że nie następuje dwukrotne dołączenie tych bibliotek przed czym ostrzegają wszelakie kursy programowania - sprawdzone.W programie głównym są też deklaracje portów NANO przydzielonych do różnych zadań (37-42).
Nie ma za to deklaracji zmiennych choć znowu wszelkie kursa poprawnego programowania absolutnie to zalecają. U mnie deklaracja zmiennej następuje w bibliotece, w której jest wykorzystywana. Dlaczego? Deklaracja zmiennej w bibliotece "xxx.h" jest widziana w programie głównym a odwrotnie NIE! Również kolejność wywołania bibliotek ma wpływ na "widzialność" zmiennej w innych bibliotekach. Zmienna by być widziana w kolejnej bibliotece musi być zadeklarowana we wcześniej wywołanej bibliotece.
Jeszcze inaczej jest z deklaracjami i wywołaniami funkcji i procedur. I znowu - procedury (funkcje) zadeklarowane w bibliotekach są widziane w programie głównym. Odwrotnie, by biblioteka mogła wywołać procedurę z programu głównego lub innej biblioteki musi na początku tej biblioteki być predefiniowana. Będzie to widać  w kolejnych bibliotekach.

Biblioteka "dallas.h"


Ta moja biblioteka podoba się mi najbardziej - jest krotka, treściwa i zawiera wszystko do obsługi czujnika temperatury DS18B20. Nawet procedury ustawień początkowych, które winny znaleźć się w pętli SETUP są tu mieszczone w osobnej procedurze setdallas() W lini 2 jest deklaracja nr portu z przyłączonym czujnikiem temperatury. Dodanie obsługi czujnika w nowym programie to

- przekopiowania biblioteki "dallas.h" do katalogu programu
- dopisaniu #include "dallas.h" na początku programu
- ustawienia nowego portu czujnika
- wywołaniu procedury setdallas() w sekcji SETUP
i wszystko
   
Biblioteka "klaw.h"



Biblioteka klaw.h to taki zlepek rożnych funkcji przypisanych do symulacji pilota świateł wszelakich w domu i na zewnątrz za pomocą sterowników ELRO. Zlepek bo oprócz deklaracji wszystkich kodów wysłanych lub odbieranych w paśmie 433 MHz (33-58)' deklaracji tabeli kodów poszczególnych pilotów (64-93) i procedur nadawania i odbioru kodów (94-129) umieściłem tu deklaracje kolorów widgetow BLYNK (12-31). Gdzieś je musiałem umieścić i trafiły tu - najczęściej bawię się kolorami zmieniając parametry przycisków w APP BLYNK. Biblioteka jest usługową wobec innych bibliotek dlatego deklarowana jest jako pierwsza. W liniach 123-125 wywołuję procedury znajdujące się w innych bibliotekach. By były one dostępne, na początku musi nastąpić ich predefinowanie (5-8).  Format predefinicji to deklaracja funkcji zakończona średnikiem - koniecznie!
Deklaracja biblioteki #include <RCSwitch.h> jest oczywista bo z niej korzysta program transmisji 433MHz. Mniej oczywista jest deklaracja #include <Arduino.h>. Bez niej kompilator "nie rozumie" wywołania wielu procedur znamiennych dla ARDUINO IDE np. Serial.print(). Choć w programie głównym takiej deklaracji nie ma to wszystko tam działa a tu nie. Pokręcone to trochę jakoś ....

Biblioteka "transmit.h"




Biblioteka "transmit.h" to protokół komunikacji pomiędzy NANO i ESP-01 oraz wywołanie procedur uruchamianych wysyłanymi z ESP komendami. Na początku predefinicje procedur użytych w programie głównym. Odpowiadają one numerom vPinow, ktore je wywołują. To prosty i czytelny sposób porządkowania katalogu wszystkich funkcji związanych ze zmianami wartości na vPinach.
 Niżej (17-44) procedura void Blynkwrite(String str2) odpowiedzialna za dekodowanie nr vPinu i jego wartości (w postaci Stringa) oraz dowiązanie do niego odpowiedniej procedury wykonawczej. To odpowiednik procedury BLYNK_WRITE(VPin) z BLYNKa.

W linii 21 wydzielam fragment przesłanego stringa zawierający nr vPin a w linii 22 zamieniam string na liczbę. Linia 23 to wydzielenie drugiego fragmentu stringa z danymi dla vPinu. Jak zostanie ona potraktowana dalej (jako INTEGER BOOL lub STRING) zależy już od mojej decyzji w procedurze obsługi vPinu.

Dalsza część kodu to część nadawcza bloku transmisji -  formatowanie stringa będącego komendą dla ESP (56-83). A więc funkcjonalnie odpowiada ona procedurze Blynk.virtualWrite(VPin, Data) z BLYNKa.
Jak widać zdefiniowałem 7 typów komend zależnych od typu przesyłanych do BLYNK danych a więc dane typu:
- tekstowe typu STRING (prefix S)
- liczbowe typu Integer (prefix I)
- sterowanie vLED (prefix L)
- sterowanie kolorem widgeta (prefix C)
- sterowanie opisem przycisku - stan OFF (prefix F)
- sterowanie opisem przycisku - stan ON (prefix N)
- inne komendy (prefix O)
Czemu aż tyle? Niestety TYLKO tyle i to jeszcze nie koniec. Chłopaki z BLYNKa odeszli od pięknej początkowej unifikacji wywoływania wszystkich funkcji widgetów poprzez procedurę Blynk.virtualWrite(VPin, Data). Teraz np. by zmienić kolor trzeba wywołać procedurę Blynk.setProperty(pin2, "color", chardata2); stąd potrzeba osobnych komend wywołujących taką funkcję. Niestety to nie koniec bo widgetów przybywa a z nimi konieczność dodawania kolejnych nowych komend. Ale to przyszłość - na razie to co jest wystarcza mi do obsługi moich programów.

I na koniec (84-116) procedury odbioru i dekodowania stringa przesłanego z ESP serialem. Wszystkie komendy wysyłane przez ESP mają prefix "V' i kończą się znakiem /r /n. Te dwa elementy są znacznikami poprawności odbieranego kodu.

Biblioteka "pompa.h"




Kolejna z opisywanych bibliotek "pompa.h" odpowiada za sterowanie pompą obiegową CO.Na początku tradycyjnie już predefinicje procedur przy czym ciekawostką jest konieczność deklaracji void pompazal(); mimo iż procedura ta znajduje się w tejże bibliotece. Problem w tym, że jest ona wywołana wcześniej (33) niż zdefiniowana (38). Czort wie dlaczego taka kolejność nie wywoluje oburzenia kompilatora w głównym programie  a w bibliotekach jak najbardziej. Dziwne to jakieś ......
Pompę uruchamia bądź czujka PIR (52) lub vPrzycisk w telefonie (45)  na czas odmierzany procedurą void licznikobiegu(). Procedura wysyła kod załączania co 10 sek aż do osiągniecia przez licznik wartości 0. Potem odlicza do wartości ujemnych by kilkakrotnie wysłać kod wyłączenia pompy (ot tak dla pewności). W trakcie odliczania na vPrzycisku wyświetlany jest stan licznika (bajer taki) i zmieniany jest kolor przycisku. Wysyłanie odpowiedniego kodu zał/wył odbywa się w liniach 29 i 40.
Jako extras dodałem widget timer pozwalający ustalić przedział czasu, w którym pompa ma prawo się załączyć od czujki PIR. Obecnie to czas między 7:00 a 23:00. Poza tymi godzinami wejście do łazienki nie generuje sygnału załączenia pompy obiegowej CO. Kąpiel o 4 nad ranem? Bez przesady!

Biblioteka "piec_co.h"





Biblioteka umożliwia ręczne zdalne zadanie temperatury (niezależnie od bezprzewodowego regulatora w pokoju) i wyłaczenie grzania gdy czujnik na module pokaże osiągnięcie zakładanej temperatury w pomieszczeniu w którym znajduje się moduł. Temperatura ustawiana jest co 0,5 oC a regulacja posiada histerezę 0,5 oC. Nie jest to jakieś super dokładne ustawianie temperatury bo czujnik ds18b20 nieźle fałszuje (już wprowadziłem korekcję -3oC) ale nie jest to podstawowy regulator ciepła w domu.
Dwa vPrzyciski w aplikacji telefonu umożliwiają nastawę temperatury odczytywaną na wyświetlaczu na vPinie 17
Procedura pieconoff()  cyklicznie porównuje temperaturę zadaną z rzeczywistą i gdy temperatura nastawiana jest co najmniej o 0,5 oC wyższa - włącza piec migając naprzemiennie kolorami wyświetlacza(36). Maksymala temperatura nastawy to 26oC minimalna 13oC

A tak to wygląda wizualnie w telefonie. O zakładkach monitorów opowiem przy okazji opisu programu w ESP-01.





Uff. To najdłuższy post i najdłuższy wygenerowany do tej pory kod. A to dopiero pierwsza część umieszczona w NANO ...........więc cd niewątpliwie musi nastąpić .........


Brak komentarzy:

Publikowanie komentarza