ArduBlock
fabryka własnych kafelków


narzędzia

Dla przypomnienia, strona domowa projektu znajduje się tu: http://blog.ardublock.com i z niej pochodzą linki do github z kodem źródłowym. Proszę pamiętać, że skompilowanych wersji ArduBlock jest w sieci sporo, na przykład taki Botland w swoim sklepie wskazuje na ardublock-jose-20130812.jar czyli jedną z wielu mutacji rozbudowaną o charakterystyczne dla danego dostawcy komponenty funkcjonalne. Wspominam o tym zwielokrotnieniu jaźni, ponieważ gdzieś tam głęboko pod spodem pracuje wspólny dla wszystkich odnóg (forków) kod baseline, a on też ulegał poprawkom i to różnej natury w różnych odnogach. Po prostu proszę uważać, co bierzemy na warsztat i na ile to oprogramowanie jest stabilne.

Proszę też zauważyć, że ArduBlock są ponadplatformowe, nie ma znaczenia czy pracujemy z Linux czy z Windows, w końcu to przenośny bytecode dla JVM. Wszystko co zobaczymy dalej powstawało na 32-bit Linux, bo tak.

Do zabawy w kafelki potrzeba nam następujących narzędzi:
  • w miarę współczesne Java SDK, u mnie v.8, 32-bit pod Linux (deb)
  • jakieś IDE do kodowania, ja osobiście preferuje NetBeans, u mnie v.8.2 pod Linux
  • Apache Maven, do zarządzania projektem
  • Git do obsługi repozytorium kodu, to już opcjonalnie

    Zakładamy folder roboczy na nasze prace i do niego klonujemy kolejno:

  • projekt OpenBlocks: 
    git clone https://github.com/taweili/openblocks.git
  • projekt ArduBlock: 
    git clone https://github.com/taweili/ardublock.git
    można także korzystać z mojego eksperymentalnego forka, niniejszym się dzielę:
    git clone https://github.com/bienata/ardublock.git
    Na projekcie OpenBlock wykonujemy:
    mvn clean package install
    i od chwili zainstalowania jego jar-a w lokalnym repozytorium maven nie będzie potrzeby dotykania tego projektu, chociaż...(**)

    Na projekcie ArduBlock wykonujemy obowiązkowo:
    mvn validate
    to pobierze nam najnowszą wersję pde.jar do lokalnego repozytorium, a ten jar jest konieczny ze względu na integrację z Arduino IDE. Potem wykonujemy testowo
    mvn clean package
    i po kilku chwilach powinniśmy mieć w folderze /target pliczek ardublock-all.jar gotowy do przegrania w trzewia naszej instalacji Arduino IDE. Jako, że ja z tych leniwych jestem, więc zmodyfikowałam nieco oryginalny POM dokładając do fazy package polecenie automatycznego skopiowania binariów do IDE:



    Ścieżka docelowa jest zaszyta na sztywno, no wiem że deko nieelegancko, proszę sobie zmienić pod swoją instalację.

    Odnośnie IDE do programowania - na stronie domowej ArduBlock wspomniana jest konfiguracja deweloperska Eclipse, no jak ktoś lubi Eclipse to proszę się męczyć, śmiało. Środowisko NetBeans bez żadnego kwękania rozpoznaje projekt maven, wystarczy do otwarcia wskazać folder projektu (ten z plikiem pom.xml) i mamy gotowe.

    Co do uruchamiania całości – oczywiście sensowność kodu generowanego przez nasze klocki musimy mieć sprawdzoną niezależnie, na zwykłym projekcie *.ino w Arduino IDE. Dopiero potem takie stabilne i odpluskwione fragmenty adaptujemy do własnych kafelków. Przyjemną cechą ArduBlock jest to, że można główne okno wtyczki uruchamiać niezależnie od Arduino IDE, a tak! Działają wszelkie operacje new/save/open a wciśniecie guzika ‘upload to arduino’ wyrzuci nam wygenerowany kod c/c++ na standardowe wyjście, czyli na konsole, będziemy wtenczas w stanie ocenić czy generowane przez plugin źródła trzymają jakikolwiek poziom.
    Uruchamiane w NetBeans to zwykłe ‘Run’ lub ‘Debug’, z poziomu konsoli możemy wykonać:
    mvn compile exec:java -Dexec.mainClass="com.ardublock.Main"
    Jeżeli zależy nam, aby trzymać się angielskiej wersji językowej (a ja to akurat preferuje i pomimo, że wakacje to dzieciaki ćwiczą na angielskojęzycznych klockach :) ) to do wywołania maszyny wirtualnej Java trzeba dodać opcje
    -Duser.country=GB -Duser.language=EN
    Wtedy też odpadną nam krajowe akcenty w przypadkowych miejscach, wynikające z niedoróbek w plikach lokalizacyjnych, skupiamy się tylko i wyłącznie na angielskojęzycznej wersji napisów.

    (**) – w ramach TODO można by zwiększyć rozmiar (szerokość) okienka popup przy wyborze z enumerowanej listy. O Ile z pinami Dn czy An nie ma problemu to nazwy Uref typu ‘Internal 1.1V’ nie mieszczą mi się w okienku i wygląda to po prostu szpetnie.

    anatomia kafelka

    Dalej będzie na przykładzie klocka Decimal To BCD. Pojedynczy, elementarny klocuszek ArduBlock (od najmniejszych stałych po wypasione mega-kafle) opisany jest następującym plikami:

  • /src/main/resources/com/ardublock/block/ardublock.properties – zawiera mapowania identyfikator=nazwa dla wszelkich widocznych napisów, nalepek i innych literałów, które mogą podlegać lokalizacji

  • /src/main/resources/com/ardublock/block/ardublock.xml – zawiera definicję kafelka czyli jego typ, opis portów wejścia-wyjścia, domyślne wartości, przypisanie do paletek grupujących klocki

  • /src/main/resources/com/ardublock/block/block-mapping.properties – zawiera mapowanie pomiędzy identyfikatorem kafelka z implementującą go klasą języka Java

  • /src/main/java/com/ardublock/translator/block/<folder>/<klasa-kafelka>.java – klasa Javy z kodem kafla zapewniająca wygenerowanie odpowiednich fragmentów kodu źródłowego w języku C/C++.

    Oczywiście, może przydarzyć się, że jeden kafelek będzie wymagał kilku wpisów do wymienionych powyżej plików, a to z uwagi na jego domyślne parametry czy inne stałe, które z tego technicznego punktu widzenia ...też są kafelkami i musimy je oprogramować.

    Na początek dodajmy sobie klocek wykonujący konwersję liczby dziesiętnej na kod BCD, to przydatna sprawa do zasilania danymi wszelkich sterowników wyświetlaczy siedmiosegmentowych typu 7447 czy takich cudnych błyskotek jak wyświetlacze 4N54. Z punktu widzenia Arduino, z poziomu c/c++ stosowną konwersję zapewni nam wyrażenie
     ( n / 10 * 16 ) + ( n % 10 )
    gdzie n to wartość dziesiętna. Oczywiście ten wariant konwertuje liczby z zakresu 0..99, czyli wynik będzie zajmował jeden bajt i miał dwie cyfry. Ubranie tego wyrażenie w funkcję C jest trywialne, proszę:
    unsigned char Dec2Bcd ( unsigned char n ) {
        return ( n / 10 * 16 ) + ( n % 10 );
    }
    
    Od naszego kafelka oczekujemy zatem że:
  • jego choćby jedyne pojawienie się w programie dorzuci gdzieś w pliku źródłowym *.ino treść funkcji, jej ciało czyli implementację, to co widać powyżej
  • każde wykorzystanie kafelka (połączenie z innymi w wykonywalną sekwencję) spowoduje wygenerowanie stosownego wywołania funkcji i przekazanie do wywołania parametrów aktualnych

    Za powyższe odpowiada właśnie klasa języka Java, którą musimy napisać, ona ma dziedziczyć z klasy TranslatorBlock i implementować jej metodę abstrakcyjną toCode(), no i oczywiście konstruktor. Spójrzmy na listing poniżej:

    /src/main/java/com/ardublock/translator/block/LM35demo/Dec2BcdBlock.java



    I tylko kilka zdań komentarza, choć jak widać filozofii żadnej tu nie ma. Po pierwsze klasę wkładamy do pakietu w obrębie tematycznego katalogu podrzędnego względem /com/ardublock/translator/block/ tu jest akurat LM35demo. Konsekwencją jest to, że w pełni kwalifikowana nazwa klasy będzie com.ardublock.translator.block.LM35demo.Dec2BcdBlock, a ta wartość będzie nam potrzebna do pliku block-mapping.properties. Klasa może nazywać się jakkolwiek, ale dobrze aby jednak spójnie z pełnioną rolą no i z sufiksem Block w nazwie. Taka jest maniera nazywania klas bloczkowych w tym projekcie i nie ma co się stawiać, ponieważ to jest całkiem ok. Konstruktor klasy jest typowy dla reszty i tak naprawdę bardzo rzadko (wcale) zachodzi konieczność dokładania tam własnych inicjalizacji.
    Za to w metodzie toCode() możemy się twórczo zrealizować, ponieważ mamy do zrobienia kilka rzeczy. Po pierwsze musimy wstrzyknąć do istniejącego źródła c/c++ treść naszej nowej funkcji, zapis jej implementacji. Służy to tego wywołanie addDefinitionCommand() publicznego pola translator dostępnego w instancji naszej klasy. Podany w wywołaniu string zostanie dołączony do wynikowego kodu źródłowego, gdzieś nad implementacją arduinowej funkcji void loop(){}.
    Ponieważ ten string zostanie wrzucony dokładnie tak jak go napiszemy, więc po pierwsze warto aby jakoś ładnie wyglądał, miał wcięcia, może jakiś drobny komentarz. Ja zapakowałam implementację funkcji Dec2Bcd w zmienną functionSourceCode, zawartość jest połamana znakami końca linii i chyba nie wygląda tragicznie. Można opcjonalnie dodać komentarz typu – wygenerowane automatycznie, nie zmieniaj – ale to chyba nadmiar troski. Przecież nikt trzeźwo myślący nie będzie dłubał w automatycznie generowanym kodzie.
    Póki co tego mechanizmu nie wykorzystuję, ale warto wiedzieć – analogiczną do metody addDefinitionCommand() jest addHeaderFile(), która pozwala dopisać się w własną treścią do tej części generowanego źródła, gdzie wskazujemy pliki nagłówkowe *.h.
    No i najważniejsze – bez względu na to , ile razy w naszej układance zostanie aktywnie wykorzystany klocuszek – dopisanie ciała funkcji (i ewentualnie nagłówków) nastąpi tylko raz! Proszę się nie martwić, generator kodu jest całkiem cwany i tego dobrze pilnuje.
    Na koniec musimy oczywiście zapewnić wygenerowanie kodu wywołania naszej nowej funkcyjki, ale zanim o tym powiem - garść informacji uzupełniających.
    Po pierwsze musimy wiedzieć, że w ArduBlock są trzy kategorie kafli: command, function i data. Klocki data służą do obsługi wszelkiej maści zmiennych i ich dalej nie rozważamy.


    Klocki command to odpowiednik znanych z jezyka Pascal procedur, w c/c++ odpowiadają im funkcje zwracające void, czyli w sumie nic. Lista parametrów może być pusta, zatem taki kafelek nie będzie miał żadnych konektorów wejściowych (socket). Co do zasady nie będzie miał także żadnych konektorów wyjściowych (plug). Jedyne co z takim klockiem można zrobić, to zaczepić go w sekwencje z innymi, kod przez niego wygenerowany zostanie wykonany zgodnie z powstałym łańcuchem wywołań.


    Klocki function to klasyczne funkcje, podstawowa cecha jest taka, że potrafią zwrócić wartość terminalem wyjściowym (plug). Lista parametrów może być pusta, jeżeli jednak potrzeba do funkcji wprowadzić dane - mamy ku temu terminale wejściowe socket.

    Teraz właśnie powinno się rozjaśnić, do czego służy wywołanie metody getRequiredTranslatorBlockAtSocket( n ) - ona po postu zwraca obiekt-kafelek zapięty do wskazanego numerkiem terminala. Na tak wskazanym obiekcie wołamy znaną już metodę toCode() - w ten sposób w generowany kod wywołania naszej funkcji wstrzykujemy kod wywołania wewnętrznego (może to być inna funkcja albo dowolnego typu stała).

    Aby nasz kafelek zaistniał w strukturze wtyczki należy pracowicie dopisać do do pliku ardublock.xml Kafelek rejestrujemy jako kolejna encję BlockGenus w sekwencji BlockGenuses, wskazujemy terminale do wymiany danych, możemy też ustalić domyślne wartości parametrów wejściowych, to akurat jest mega przydatne dla dzieciarni, ponieważ mniej się wtedy gubią. Kolejna modyfikacja to dopisanie nowego kafelka do już istniejącej paletki, ewentualnie założenie zupełnie nowej - służy ku temu encja BlockDrawer w sekwencji BlockDrawerSet. W obu przypadkach mamy możliwość zdecydowania o kolorystyce kafli. Nie ma tu w sumie żadnych jawnych czy niejawnych reguł i możemy uprawiać dowolną pstrokaciznę, choć warto mieć na uwadze, że projekty inaczej wyglądają na ekranie monitora, a inaczej rzucane na ścianę projektorem.
    Wszelkie napisy na kafelku rejestrujemy w pliku ardublock.properties, pamiętając że prefiks bd. dotyczy napisów na paletce (d - od drawer), a prefiks bg. należy do kafelka jako takiego (g-od genus). Jeżeli ktoś pragnie przygotować polską lokalizację - adekwatny plik ardublock_pl także jest w projekcie.

    Wszelkie opisywanej powyżej zależności przedstawia następująca plansza poglądowa:



    Kolejną ważna sprawą są kafelki, których domyślne wartości wejściowe wybieramy z rozwijanej listy, takie jakby enumeracje. Przykład pod ręką to nowy kafel Set Analog Reference, którego port wejściowy inicjujemy jedną ze stałych numerycznych DEFAULT, EXTERNAL, INTERNAL. Oczywiście, można pójść na łatwiznę i zostawić tam dowolną wartość integer, ale dla początkujących to nie będzie wygodne, trzeba będzie pamiętać jaka liczba co oznacza, a tak mamy ładnie napisane. Listy wyboru robi się całkiem łatwo, w sensie nie jest to koncepcyjnie zbyt skomplikowane - ot wartości, które chcemy zgrupować rejestrujemy w ardublock.xml encjami FamilyMember, całość zamykamy w listę BlockFamily. Oczywiście mamy obowiązek zarejestrować kafelki z elementarnymi wartościami do wyboru jako BlockGenus, ale nie dopisujemy ich do paletki BlockDrawer, no chyba że strasznie potrzebujemy także i je mieć widoczne na wysuwanej z boku tacce, z reguły tak nie robimy. Kolejna rzecz do ogarnięcia to napisanie tylu klas ile mamy nowych enumerowanych wartości, dla napiecia referencyjnego to trzy sztuki, ale za to mega proste - implementacja ich metod toCode() zwraca po prostu napisy INTERNAL,EXTERNAL, DEFAULT, przykład poniżej:

    /src/main/java/com/ardublock/translator/block/LM35demo/ExternalConstBlock.java



    Koneksje pomiędzy sekcjami w plikach oraz kafelkiem w przypadku enumerowanych wejść przedstawia kolejna plansza:



    Przy okazji zwróćmy uwagę, że kafelek Set Analog Reference jest typu command, niczego mądrego nie zwraca, za to generuje kod źródłowy wywołania bibliotecznej funkcji analogReference()

    --- ❦ ---

    Tym razem w pisance mej wystąpi Arduino Nano, nie mogłam się tylko na początku zdecydować - czy nasz rodzima podróbka, czy oryginalny z Chin. Stanęło na modułku od keyestudio, z prozaicznej przyczyny takiej, że jest ładniejszy.



    Proszę, oto wzmiankowane w artykule w EdW przecudne zaiste wyświetlacze 4N52, wyglądają kosmicznie i choć sporo prądu sobie życzą to bardzo wdzięczne do programowania.





    Zabudowa w testowy układzik i kilka filmów, jak działają na żywo te śliczności.












    --- ❦ ---

    Termometr występujący w artykule oczywiście w komplecie z myszą i Meratronikami, do weryfikacji czy przetwornik A/C w Ardu prawdę mówi.




    Jak mamy okazję złapać LM35 w blaszanej obudowie to polecam, naprawdę atrakcyjniej wizualnie to wygląda niż plastikowe pospólstwo.



    Budowa prototypu termometru. Sztuka w tym, aby wszelkie komponenty weszły razem w kadr, stąd LM35 na pałaku z drucików.





    Macanie paluchami LM35 uznałam za nieestetyczne, zatem celem podniesienia temperatury czujnika i pokazania, że cała instalacja jednak naprawdę działa w roli grzejnika występuje lokówka Philips tylko ze zdjętą końcówką. Epizod z kręceniem włosów mam już za sobą, a lokóweczka służy do celów nazwijmy to - technologicznych. Temperatura na wylocie dyszki nie przekracza 70'C jest zatem o niebo bezpieczniejsza niż lutownica hot-air, którą przez nieuwagę można by coś po prostu spalić.









    --- ❦ ---

    Filmiki wspomniane w artykule w EdW, kolejno dla omawianych programów-układanek w Ardublock, ich archiwum tu: edw_ardublock_demo_123.zip.

    edw_ardublock_demo_1.abp





    edw_ardublock_demo_2.abp





    edw_ardublock_demo_3.abp






    --- ❦ ---

    W artykule w EdW wspomniałam, że jest realnym zbudowanie sterowania kostką CD4094 przy pomocy dostepnych elementarnych klocków, zaznaczyłam także, że (w mojej ocenie) wyjdzie z tego niekiepski potworek. Zanim go zobaczymy, dla ustalenia uwagi najpierw przypomnę schemat podłaczenia 4094 do Ardu:



    Mamy jak zakładam jasność, że aby na wyjściach Q8..Q1 pojawiła się ośmiobitowa liczba z Ardu, to należy podawać jej bity na wejście DATA od najstarszego począwszy. Po ustawieniu bitu DATA generujemy impuls zegarowy CLK, który wsunie daną w rejestr. Po ośmiu takich cyklach wysyłamy impuls STROBE, który przepisze dane z wnętrza układu do rejestru wyjściowego, na fizyczne wyprowadzenia kostki. No, i taką właśnie sekwencję operacji musimy z grubsza poukładać z kafelków. Wzorowanie się na kodzie w C jest poniekąd celowe, ale tylko w sensie odtworzenia koncepcji, próbując narysować kod jak niżej tak na wprost, jeden do jednego - to raczej polegniemy.
    void cd4094sendByte( unsigned char data ) {              
        for (int bit = 0; bit < 8; bit++ ) {                 
            digitalWrite ( cd4094_dataPin, data & 0x80 );  
            data <<= 1;                                      
            digitalWrite ( cd4094_clockPin,  HIGH );
            digitalWrite ( cd4094_clockPin,  LOW );          
        }                                                    
        digitalWrite ( cd4094_strobePin,  HIGH );            
        digitalWrite ( cd4094_strobePin,  LOW );             
    }
    
    Problem polega na tym, że w kafelkach ArduBlock nie ma ani przesunięcia bitowego ani operacji and na zmiennych numerycznych, jest tylko na logicznych, typu boolean. No i tu zasadniczo klops. O ile przesunięcie bitowe w lewo możemy jeszcze zasymulować kolejnymi mnożeniami przez 2, to brak iloczynu logicznego na integerach skutecznie nam utrudni życie. Nie ukrywam, też miałam sporą zachwostkę jak temat zaatakować ale z pomoca przyszły...algorytmy konwersji wartości dziesiętnych na binarne, a tak! :). Przecież do sterowania kostką 4094 potrzebujemy wiedzy o stanie kolejnych bitów w danej wejściowej, a taka wiedza jest dostępna w trakcie konwersji dec-bin. Proszę, oto do poczytania opracowanie, które bardzo mi pomogło: How to Convert from Decimal to Binary, akapit 'Descending Powers of Two and Subtraction'. Z wykorzystywanych tam porównywania, odejmowania i potęgowania - do wszystkiego kafelki są i program jakoś powstał.
    Program główny jest dość banalny, w cześci setup woła procedurkę initTestValuesArray, która inicjuje dane testowe do wyświetlania i w środku jest dość nudna. W sekcji loop wybierny jest kolejny element liczbowy z zainicjowanej tabelki i przekazywany do wywołania procedury sendByteTo4094. I najciekawsza z tego jest właśnie rzeczona procedurka, to graficzna implementacja algorytmu konwersji na bin metodą malejących potęg i odejmowania. W miejscu gdzie dla kolejnego odliczonego bitu z przetwarzanej danej mam już jak na dłoni jego wartość - true/false - wystawiam to na pin Ardu, niech leci do kostki 4094. Widoczne kafle serial print służyły do uruchomienia i przetestowania układanki, napisy na terminalu dobrze ilustrują prace pętli, stąd zostawiłam je na obrazkach, choć pisanie portem szeregowym odrobinę zamula programik.





    Tradycyjnie filmik jak powyższy programik sprawuje się na żywo, ze względu na rozmiar okna - nagrywany cały desktop.



    No i mówiłam, że będzie potworek? A proszę sobie teraz wyobrazić, jakby to wszystko cudnie wyglądało, gdyby nie było w ArduBlock modularyzacji czyli możliwości tworzenia i wywoływania własnych procedur...

    #slowanawiatr, lipiec 2018

  • Natasza Biecek 2004-2018/~, e-mail