Komputer na bazie RP2040 ze wsparciem dla BASIC-a

Komputer na bazie RP2040 ze wsparciem dla BASIC-a

Rok 1976 był czasem interpreterów BASIC-a. Legendarny magazyn Dr. Dobb’s opublikował wiele artykułów, projektów i implementacji interpreterów Tinybasic. Autor poniższej konstrukcji, 45 lat później, zatęsknił za tymi czasami i postanowił opracować własny interpreter BASIC-a zupełnie od podstaw. Dodatkowo, system uzupełniła platforma sprzętowa, zbudowana na mikrokontrolerze Raspberry Pi RP2040, która pozwoliła na uruchomienie tego interpretera.

Punktami wyjścia do prac nad interpreterem były interpretery Palo Alto i Apple 1 BASIC. Potem projekt znacznie się rozrósł, jak wskazuje autor, Stefan Lenz. Jest to obecnie niemal standardowy interpreter BASIC-a typu Dartmouth z opcjonalnym wsparciem dla liczb zmiennoprzecinkowych, łańcuchami, tablicami i możliwościami komunikacji w systemach IoT dla różnych mikrokontrolerów. Ma sterowniki ekranu, dostęp do sprzętu i kilka innych nietypowych dla BASIC-a funkcji.

Podstawowy język BASIC bazuje na specyfikacji Dr. Wanga, która nazywana jest Palo Alto BASIC. Autor zastosował tę specyfikację na podstawie artykułu opublikowanego przez Rogera Rauskolba. Nie użył jednak kodu z żadnego źródła, tylko samej specyfikacji języka.

Drugim źródłem jest podręcznik Steva Wozniaka, dotyczący Apple 1 BASIC. Interpreter ten jest kompatybilny z Apple Integer BASIC z 1976 roku. Korzysta z logiki ciągów Apple Integer BASIC, która nie jest kompatybilna z MS lub Dartmouth BASIC. Autor wspomina również o legendarnej książce 101 BASIC Computer Games. Mówi, że było to jedno z głównych źródeł inspiracji dla Apple Integer BASIC. Finalnie, wiele zaczerpnięte zostało z Altair BASIC, które szybko stało się standardem dla interpreterów języka BASIC po 1976 roku, ze względu na jego kompletność i użyteczność. Za tę pracę inżynierii oprogramowania trzeba podziwiać 20-letniego Billa Gatesa – oryginał interpretera mieścił się w 4 kB pamięci i miał wsparcie dla arytmetyki zmiennoprzecinkowej.

Implementacja języka w tym projekcie wyszła poza zestaw Apple Integer BASIC i oferuje obsługę arytmetyki zmiennoprzecinkowej, drukowanie, obsługę terminali, komunikację z Arduino I/O i zapis/odczyt z pliku. Dodano również funkcje sieciowe i niektóre funkcje potrzebne do integracji z systemami Internetu Rzeczy (IoT). Interpreter działa na platformie Arduino AVR 8-bitowej i 32-bitowej. Obsługiwane są mikrokontrolery takie, jak ESP8266 i ESP32, a także mikrokontrolery SAMD oraz MbedOS. Obsługiwany jest również Raspbian, w tym niektóre podstawowe operacje we/wy za pośrednictwem biblioteki wirePI. Obecnie autor udostępnił wersję 1.3. Zawiera ona pliki binarne dla systemów DOS, Mac, Windows i Raspbian.

Artykuł dotyczy budowania samodzielnego komputera z interpreterem BASIC, omówionym powyżej. Zawiera on niedrogie komponenty, które są łatwo dostępne i można je zintegrować bez większych trudności. System bazuje na mikrokontrolerze Raspberry Pi RP2040. Alternatywnie, zamiast tej platformy, można zastosować moduły Arduino MKR lub dowolną płytkę z ESP32. Omówiony powyżej system wyposażony jest w kartę SD i system plików, kolorowy wyświetlacz (rozdzielczość 480×320 px) obsługujący 30×20 znaków tekstowych z domyślną czcionką 16×16. Ma wbudowany zegar czasu rzeczywistego z podtrzymaniem bateryjnym do dokładnego pomiaru czasu oraz interfejs dla klawiatury PS/2 do wprowadzania danych. Opcjonalnie można podłączyć do niego drukarkę termiczną (przez interfejs szeregowy) lub sensory środowiskowe itp. (przez interfejs I²C). Do jego zasilania można użyć dowolnego zasilacza od 7 V do 12 V, ponieważ system ma własny stabilizator napięcia 5 V. Do dyspozycji jest 64 kB użytecznej dla BASICa pamięci oraz 2 GB miejsca na dysku.

Dlaczego więc warto uruchamiać BASIC na mikrokontrolerze? Każdemu, kto programował na mikrokontrolerach z lat 80. (a nawet późniejszym – wystarczy wspomnieć Bascoma dla AVR, szalenie popularnego w Polsce) brakuje jednej rzeczy na nowoczesnym komputerze.

Jest to łatwość użycia, gdy chcemy zrobić coś szybko. Można w BASIC-u interaktywnie wpisać mały program, debugować go i krok po kroku rozszerzać. Bez kompilatora, IDE i innych rzeczy, które w takiej sytuacji częściej przeszkadzają, niż pomagają. Wiele programów używanych w środowisku IoT jest naprawdę prostych. To często mechanizmy działania typu „odczytaj czujnik i prześlij dane”. Można to bardzo dobrze oprogramować za pomocą tego naprawdę prostego języka programowania.

Komputer, który jest głównym bohaterem tej historii, oparty jest na połączeniu Arduino RP2040 i omówionego interpretera BASICa w pełni funkcjonalny system komputerowy wspierający język programowania z tablicami, grafiką, obsługą zmiennych zmiennoprzecinkowych, a także z dostępem do kompletnego zestawu wyjść I/O przez Arduino i kilkoma innymi przydatnymi funkcjami.

Potrzebne elementy

Omawiany komputer można zbudować na dużej płytce stykowej prawie bez lutowania (poza złączem PS/2, które generalnie trzeba przylutować). Najlepiej jest użyć płytki 10×15 cm, aby uzyskać naprawdę stabilną konfigurację sprzętową. Do budowy systemu potrzebne będą:

  • moduł Arduino RP2040, który łączy wszystkie elementy i ma na pokładzie mikrokontroler,
  • shield dla Arduino z wyświetlaczem na sterowniku ILI9488. Te kontrolery korzystają z interfejsu SPI do komunikacji, a moduł ma także gniazdo kart SD i opcjonalnie wejście dla ekranu dotykowego, które jednak nie będzie tutaj potrzebne. Zamiast tego można zastosować inny moduł, należy się tylko upewnić, że będzie to moduł z komunikacja poprzez SPI,
  • konwerter poziomów napięć,
  • klawiatura z interfejsem PS/2 i gniazdko PS/2,
  • duża płytka stykowa lub płytka uniwersalna o wymiarach 10×15 cm,
  • kable do płytek prototypowych, jeśli układ budujemy na płytce stykowej,
  • karta SD (opcjonalnie),
  • zegar czasu rzeczywistego – moduł oparty na układzie DS1307 lub DS3231 (opcjonalnie) ,
  • stabilizator napięcia 7805, kondensatory 0,1 μF i 0,33 μF oraz gniazdo zasilania, jeśli chcemy korzystać z zasilania innego, niż za pomocą USB (opcjonalnie),
  • drukarka termiczna z interfejsem szeregowym (opcjonalnie),
  • trochę sklejki i mała płytka stykowa na podstawkę plus zasilacz do układu (USB lub inny, zależnie, od wybranego rodzaju zasilania).

Oprogramowanie

Całe oprogramowanie potrzebne do działania omawianego systemu znajduje się w repozytorium na GitHubie. Składa się ono z wielu elementów i opisanie go na łamach tego artykułu jest raczej bezcelowe – każdy zainteresowany źródłami może przeanalizować je w repozytorium.

To, co jest istotne, z punktu widzenia budowy urządzenia, to plik definicji sprzętu hardware-*.h, gdzie zdefiniowane są poszczególne komponenty sprzętowe, w jakie wyposażono komputer. Taka architektura oprogramowania pozwala na bardzo elastyczne konstruowanie sprzętu w omawianym systemie. W dalszej części, omawiając poszczególne komponenty sprzętowe, opisany zostanie, także sposób ich definiowania w tym pliku.

Jeśli chodzi o ustawienia w pliku ze szkicem oprogramowania (TinybasicArduino.ino) należy edytować sekcję definicji języka:

#define BASICFULL
#undef BASICINTEGER
#undef BASICSIMPLE
#undef BASICMINIMAL
#undef BASICTINYWITHFLOAT

Jedynie opcja BASICFULL powinna być włączona. Pozwoli to na wykorzystanie wszystkich możliwości języka.

Budowa urządzenia

Zaprezentowany komputer jest niezwykle modułowy. W dalszej części artykułu pokazane są poszczególne elementy, z jakich składa się system, także te opcjonalne. Oprócz sposobu podłączenia poszczególnych modułów, zawarto także informacje na temat ich definicji w pliku konfiguracyjnym.

Podłączenie ekranu

Moduł wyświetlacza ILI9488 został wyposażony w rząd pinów z tyłu (fotografia 1). Po prawej stronie znajdują się złącza zasilania, wyświetlacza i ekranu dotykowego (jeśli dany moduł posiada panel dotykowy). Po lewej stronie znajdują się 4 złącza do karty SD (patrz opis w dalszej części artykułu). W tym projekcie interfejs dotykowy nie jest używany.

Fotografia 1. Wygląd zastosowanego wyświetlacza od strony złączy

Domyślnie interpreter BASIC-a będzie używał ekranu w trybie poziomym. W układzie pokazanym na fotografii 2 kartę SD można wyjąć od góry, a na dole płytki jest miejsce na inne elementy. Należy następnie zamontować również moduł Arduino tak, aby złącze zasilania znajdowało się na górnej stronie płytki. Ułatwia to okablowanie i montaż komputera.

Fotografia 2. Podłączenie modułu wyświetlacza do układu

Wyświetlacze ILI9488 to układy zasilane 3,3 V, podobnie jak Arduino RP2040. Oznacza to, że nie są potrzebne żadne konwertery poziomów. Wystarczy podłączyć pin zasilania i masy ekranu do pinów modułu z mikrokontrolerem. Aby sterować wyświetlaczem, pin CS musi być połączony z pinem 9 modułu RP2040, DC z pinem 8, a RESET z pinem 7. Linie magistrali SPI w module z mikrokontrolerem to 13 dla SCK, 12 dla MISO i 13 dla MOSI. Są to standardowe ustawienia Arduino dla tego układu. Należy połączyć te piny z odpowiednimi pinami na module wyświetlacza. Pin LED może być używany do kontrolowania jasności wyświetlacza. Można podłączyć go albo do potencjometru, albo do pinu analogowego A3. Ten ostatni pozwala kontrolować jasność wyświetlacza z poziomu programu w BASIC-u.

Po wykonaniu połączeń koniecznie trzeba włączyć odpowiednią funkcje w pliku opisu sprzętu hardware-arduino.ino gdzie włączamy następującą opcje:

#define DISPLAYCANSCROLL
#define ARDUINOILI9488

Wszystko powinno być oddefiniowane (#undef), oprócz ARDUINOILI9488 oraz DISPLAYCANSCROLL. Po skompilowaniu i wgraniu szkicu do mikrokontrolera, interpreter powinien wydrukować na ekranie zachętę do podania kodu przez interfejs szeregowy. Jeśli teraz podamy prosty kod:

PRINT &2, "Hello World"

Na ekranie powinno się ukazać Hello World, jeśli wszystko podłączone jest dotychczas prawidłowo.

Podłączanie karty SD lub LittleFS

W przypadku samodzielnego komputera potrzebny jest magazyn plików. Istnieją dwie opcje dla tego systemu. Można użyć gniazda karty SD modułu wyświetlacza lub wewnętrznej pamięci Flash mikrokontrolera z systemem plików LittleFS. Biorąc pod uwagę niestabilność i ograniczenia kart SD, LittleFS wydaje się być lepszy, jako system plików dla tego komputera.

Jeśli chodzi o karty SD, to trzeba uważać, gdyż niektóre działają na Arduino ze standardową biblioteką, a niektóre nie. Kartę najlepiej jest sprawdzić pod kątem błędów i sformatować na komputerze PC. Jeśli system pokazuje sporadyczne błędy, należy sprawdzić połączenia, ale także warto wypróbować inną kartę. Karty są bezpośrednio podłączone do magistrali SPI za pomocą tylko kilku rezystorów. Autor zastosował kartę o pojemności 1 GB firmy Kingston.

Aby użyć gniazda karty SD, trzeba podłączyć linie SD_MISO, SD_MOSI i SD_SCK dla portu SD po prawej stronie wyświetlacza do pinów magistrali SPI, jak powyżej. Dodatkowo należy podłączyć pin 10 modułu z mikrokontrolerem do pinu SD_CS. Po podłączeniu nowego modułu należy otworzyć plik hardware-arduino.h i dodać definicję kart SD do sekcji definicji sprzętu:

#define ARDUINOSD

W projekcie dołączone są standardowe biblioteki Arduino SD. Po skompilowaniu szkicu można wgrać go do układu i wpisać prosty program przez port szeregowy:

10 PRINT &2, "hello world"
20 PRINT "hello world"

A następnie wpisać:

SAVE
CATALOG

BASIC powinien wyświetlić zawartość karty SD po drugim poleceniu. Zapisany program pojawi się, jako file.bas.

Jeśli któreś z tych poleceń powoduje błąd, należy sprawdzić połączenia pomiędzy modułami oraz wypróbować inną kartę SD. Jeśli nie potrzebujemy wymiennego magazynu plików lub nie mamy odpowiedniej karty SD, najlepiej użyć wewnętrznej pamięci Flash. W takiej sytuacji w pliku konfiguracyjnym ustawiamy zamiast ARDUINOS definicję RP2040LITTLEFS:

#undef ARDUINOSD
#define RP2040LITTLEFS

Po skompilowaniu i wgraniu programu można skorzystać z komendy FDISK, aby wstępnie sformatować wewnętrzną pamięć plików LittleFS. Po tym działaniu wszystkie polecenia pliku mogą być używane w pamięci wewnętrznej. Domyślny rozmiar systemu plików to 1 GB.

Można to zmienić za pomocą parametru RP2040_FS_SIZE_KB w kodzie źródłowym. Dostępne są następujące komendy:

  • SAVE "nazwa pliku" – zapisuje program,
  • LOAD "nazwa pliku" – ładuje program,
  • CATALOG – wyświetla zapisane pliki,
  • DELETE – usuwa plik,
  • OPEN i CLOSE – służą do otwierania i zamykania plików do odczytu.

Podłączenie pamięci EEPROM

Jeśli nie mamy pod ręką odpowiedniej karty SD, a nadal chcemy dodać wymienne urządzenie pamięci masowej do swojego komputera, moduły EEPROM są świetną alternatywą. Przykładowy moduł tego rodzaju pokazany jest na fotografii 3. Linie SDA i SCL podłączane są do odpowiednich pinów mikrokontrolera – A4 i A5. Dodatkowo, do pamięci trzeba podłączyć zasilanie do linii VCC i GND. Sugerowane moduły powinny mieć 32 kB lub 64 kB pamięci.

Fotografia 3. Moduł z pamięcią EEPROM

Aby skompilować program z obsługą pamięci EEPOM należy pobrać bibliotekę systemu plików EEPROM z repozytorium (https://github.com/slviajero/EepromFS). Biblioteka ta tworzy bardzo prosty system plików na dowolnej pamięci EEPROM podłączonej przez interfejs I²C. Może być używana bez interpreter BASIC-a, jako samodzielny komponent. System plików dzieli pamięć EEPROM na kilka slotów o równej wielkości i przechowuje w niej pliki. API jest napisane w stylu C.
Aby wykorzystać zewnętrzną pamięć EEPROM należy zamiast ARDUINOSD lub RP2040LITTLEFS, ustawić flagę kompilatora ARDUINOEFS:

#define ARDUINOEFS

Trzeba również zdefiniować adres I²C i rozmiar pamięci. To wykonywane jest w następującej sekcji:

#define EEPROMI2CADDR 0x050
#define RTCI2CADDR 0x068
#define EFSEEPROMSIZE 32767

Po uruchomieniu interpretera BASIC-a można sformatować system plików EEPROM korzystając z komendy FDISK X, gdzie X to ilość slotów na pliki, jaka ma być utworzona. Każdy slot ma pojemność 8 kB. Polecenia plików, takie jak CATALOG, SAVE, LOAD, OPEN, CLOSE i DELETE, mogą być teraz używane w EEPROM – działają tak samo, jak opisano wcześniej.

Moduły EEPROM można łatwo wymieniać między różnymi komputerami. Przypominają trochę moduły programowe starych komputerów ATARI. Zaletą modułów EEPROM jest niskie zużycie energii. Alternatywnie, jako system plików można użyć EEPROM zegara czasu rzeczywistego.

Podłączenie zegara czasu rzeczywistego

Płytka zegara czasu rzeczywistego (fotografia 4) jest podłączona do magistrali I²C komputera. Płytkę można umieścić tuż pod Arduino nieco w prawo, jak pokazano na rysunku.

Fotografia 4. Moduł zegara RTC z baterią podtrzymującą

Moduł podłączamy do interfejsu I²C oraz oczywiście do linii zasilania (VCC i GND). SDA zegara jest połączone z pinem A4, a SCL z A5. To jest standardowe wyjście I²C z tego modułu. Następnie w pliku konfiguracji sprzętowej ustawiamy odpowiednie flagi, aby włączyć obsługę dodanego sprzętu:

#define ARDUINORTC
#define ARDUINOWIRE

Aby szkic skompilował się poprawnie trzeba zainstalować bibliotekę uRTC. Można ją pobrać za pomocą menedżera bibliotek Arduino.

Finalnie, przed kompilacją, należy jeszcze skonfigurować ustawienia I²C dla układu zegara czasu rzeczywistego. W przypadku zastosowania omawianego modułu w szkicu Arduino wpisujemy:

#define EEPROMI2CADDR 0x050
#define RTCI2CADDR 0x068
#define EFSEEPROMSIZE 32767

Wartość 0x068 to standardowa wartość adresu zegara czasu rzeczywistego I²C. Jeśli zastosowany moduł ma inny adres, trzeba go tutaj zmienić. Jeśli używany w systemie EFS i mamy dodatkowy EEPROM podłączony do magistrali I²C, należy sprawdzić, czy nie ma konfliktu adresów. Dodatkowo, niektóre zegary mają swoją pamięć EEPROM, dostępną pod adresem 0x057 lub 0x050. W systemie można mieć tylko jedną pamięć EEPROM.

Gdy w wierszu poleceń pojawi się komunikat BASIC, należy wpisać:

@T(0)=0
PRINT @T$

Pierwsze polecenie inicjuje zegar, a drugie polecenie powinno wydrukować na ekranie ciąg aktualnej daty i godziny.

Podłączenie klawiatury

Omówione elementy działały z zasilaniem 3,3 V. Niestety klawiatury PS/2 wymagają napięcia 5 V. Aby udostępnić 5 V na płytce należy zewrzeć połączenie z tyłu płytki z mikrokontrolerem, jak pokazano na rysunku 5. Połączenie tego elementu pozwala wystawić 5 V z USB na wyprowadzeniu 5 V modułu. Uwaga – połączenie go z jakimkolwiek innym pinem prawdopodobnie uszkodzi płytkę.

Fotografia 5. Lokalizacja zworki VUSB na płytce z RP2040

Do podłączenia klawiatury potrzebny będzie konwerter poziomów (fotografia 6).

Fotografia 6. Moduł konwertera poziomów napięć

Podłączamy do niego zasilanie 3,3 V, GND oraz piny 2 i 5 modułu z mikrokontrolerem do strony 3,3 V konwertera poziomów. Podłączamy również linie 5 V i GND do strony 5 V konwertera poziomów. Następnie podłączamy gniazdo PS/2 tak, jak pokazano na rysunku 1. Pin zegara po prawej stronie gniazda przechodzi do pinu konwertera poziomu od strony 5 V, który trafia po drugiej stronie konwertera na pin 2 danych mikrokontrolera. Pin danych jest połączony z pinem 5 mikrokontrolera, oczywiście przez konwerter poziomów. Zasilanie i masa układu są oczywiście odpowiednio połączone.

Rysunek 1. Sposób podłączenia złącza klawiatury

Teraz należy ponownie rozszerzyć definicje sprzętu w pliku hardware-arduino.h:

#define ARDUINOPS2

Do skompilowania potrzebna będzie biblioteka Arduino PS2. Można użyć np. źródła autora systemu z jego repozytorium (https://github.com/slviajero/PS2Keyboard). Po skompilowaniu i wgraniu szkicu do układu można podłączyć klawiaturę i zrestartować mikrokontroler.

Kontrolki stanu klawiatury powinny migać po ponownym uruchomieniu. Klawiatury PS/2 często wymagają dużej mocy, więc należy dostarczyć odpowiednią ilość prądu do układu. Czasami klawiatura inicjuje się bardzo wolno. W takiej sytuacji dobrze jest wypróbować kilka różnych klawiatur. Może być konieczne ponowne uruchomienie Arduino poprzez odłączenie zasilania i ponowne podłączenie, jeśli wystąpi problem z klawiaturą.

Gdy klawiatura mignie diodami, można przetestować ją za pomocą programu w BASIC-u:

10 FOR I=1 TO 100
20 GET &2, A: PRINT A
30 DELAY 500
40 NEXT

Wpisanie RUN uruchamia program. Wartości ASCII naciskanych przycisków będą wysyłane przez port szeregowy i mogą być podejrzane za pomocą monitora portu szeregowego w Arduino IDE.

Zasilanie

Jeśli chcemy zbudować naprawdę niezależny komputer to musi on być niezależny od zasilania z USB. Można oczywiście użyć zasilacza USB i odpowiedniego kabla albo też dodać gniazdo zasilania dla zasilacza wtyczkowego i stabilizator napięcia. Obwód stabilizatora liniowego jest prosty. Stabilizator napięcia 7805 ma jedno wejście, które podłączone jest do wtyczki zasilania. Plus znajduje się na wewnętrznym bolcu zasilacza, a minus na zewnętrznej części wtyczki. Pin wyjściowy stabilizatora jest połączony bezpośrednio z linią 5 V w układzie. Tylko konwertery poziomów i klawiatura używają napięcia 5 V. Nie ma zabezpieczenia pinu USB komputera tym obwodem – nie można podłączać jednocześnie zasilacza i komputera. Zalecane są dodatkowo dwa kondensatory filtrujące.

Drukarka

Małe drukarki termiczne są bardzo przydatne do szybkiego uzyskania kopii danych zebranych przez Arduino. Te drukarki termiczne zawierają logikę 5 V i zazwyczaj wymagają zasilania 5 V/2 A. Podłączenie drukarki powinno odbywać się poprzez konwerter poziomów.

W tym celu należy podłączyć pin TX i RX Arduino do strony 3,3 V konwertera poziomów. Następnie należy podłączyć stronę 5 V tych pinów do odpowiedniej wtyczki. Można do tego celu użyć standardowych kabli i wtyczek stereo audio. To może być niekonwencjonalne, ale te kable są tanie, dobrej jakości i dobrze ekranowane. Szybkość transmisji 9600 może być przez nie bezpiecznie przesyłana. Uziemienie jest połączone z zewnętrzną stroną wtyczki, TX i RX przez wewnętrzną stronę. Należy upewnić się, że TX Arduino jest połączony z RX drukarki i odwrotnie.

Aby aktywować drukowanie, należy przekompilować interpreter po skonfigurowaniu pliku hardware-arduino.h:

#define ARDUINOPRT

Teraz ARDUINOPRT jest zdefiniowany. Dzięki temu Serial1 jest dostępny w BASIC, jako strumień wyjściowy &4. Dane są wysyłane do drukarki za pomocą polecenia:

PRINT &4, "Hello World"

W instrukcji do samego języka, jaka dostępna jest w repozytorium z projektem, znaleźć można więcej informacji na temat strumieni wyjściowych BASIC i użycia modyfikatora &.

Sieć bezprzewodowa

Do interpretera BASIC dodano bardzo podstawową obsługę Internetu Reczy (IoT). Aby podłączyć system do sieci, należy otworzyć plik wifisettings.h, a w nim wpisać w następującym miejscu:

const char* ssid = "";
const char* hasło = "";
const char* mqtt_server = "test.mosquitto.org";
const short mqtt_port = 1883;
bajt mac[] = {0xDE, 0xAD, 0xBE, 0xE9, 0xE9, 0xE9};

Identyfikator SSID i hasło do naszej sieci. Nie jest to zbyt bezpieczne podejście, więc nie nadaje się do większych instalacji. Następne dwie linijki zawierają adres i port serwera MQTT. Obecnie obsługiwane są tylko nieuwierzytelnione i niezaszyfrowane serwery MQTT. Ponownie, nie jest to bezpieczne rozwiązanie dla typowych systemów IoT instalowanych w warunkach rzeczywistych. Wstępnie skonfigurowany serwer to serwer testowy Mosquitto.

Po skonfigurowaniu tego pliku należy otworzyć plik hardware-arduino.h i wprowadzić następującą zmianę:

#define ARDUINOMQTT

Do działania potrzebna jest biblioteka Pubsub MQTT i biblioteki Arduino WifiNINA z menedżera bibliotek Arduino. Po ich doinstalowaniu można przekompilować szkic i wysłać go do mikrokontrolera.

Po ponownym uruchomieniu systemu wpisujemy mu komendę NETSTAT. Jeśli komputer jest podłączony do sieci, wyjście powinno zawierać informacje na temat połączenia i dostępu do serwera MQTT. Teraz komputer jest gotowy do odbierania i wysyłania wiadomości MQTT.

Typowy program do wysyłania bez obsługi błędów może wyglądać tak:

10 OPEN &9, "iotbasic/testdata", 1
20 PRINT &9, "data:", AREAD(AZERO)
30 DELAY 2000
40 GOTO 20

Ten program otworzy temat MQTT iotbasic/testdate do zapisu, wypisze ciąg data: i wartość analogową na A0, odczeka dwie sekundy, a następnie zmierzy i zapisze wartość ponownie.

Dodawanie sensorów I²C

Interpreter BASIC ma wbudowany zestaw czujników i może uzyskać dostęp do magistrali I²C bezpośrednio za pomocą poleceń BASIC-a. Na listingu 1 pokazano przykładowy program, który powinien wykryć zegar czasu rzeczywistego na magistrali I²C. Podłączając dowolny inny czujnik I²C, on także zostanie wykryty. Polecenia BASIC GET, PUT, PRINT i INPUT na kanale I/O &7 umożliwiają bezpośredni dostęp do urządzeń I²C. Czujniki mogą być odczytywane na poziomie bajtów, a dane mogą być wykorzystywane w programach.

Listing 1. Przykładowy program, który wykrywa urządzenia na magistrali I2C

10 REM "Identify devices on the I2C bus"
20 REM ""
100 REM "the setup()"
110 FOR I=1 TO 127
120 @S=0
130 REM "Try to open a device and send a byte"
140 OPEN &7, I
150 PUT &7, 0
160 IF @S=0 THEN PRINT "device found on", #3, I;": ";:GOSUB 500
190 NEXT
200 PRINT
210 END
500 REM "Search the device name"
505 IF I=56 THEN PRINT "AH10" : RETURN
510 IF I=60 THEN PRINT "Oled" : RETURN
520 IF I>=80 AND I<=87 THEN PRINT "EEPROM" : RETURN
530 IF I=104 THEN PRINT "Real Time Clock" : RETURN
540 IF I=118 OR I=119 THEN PRINT "BMP/E280" : RETURN
590 PRINT "Unknown"
600 RETURN

Niektóre czujniki są już wstępnie zdefiniowane w języku BASIC. Można je aktywować, jako część kodu. W hardware-arduino.h w sekcji ARDUINOSENSORS znajdują się flagi, odpowiedzialne za sensory:

#ifdef ARDUINOSENSORS
#undef ARDUINODHT
#define DHTTYPE DHT22
#define DHTPIN 2
#undef ARDUINOSHT
#ifdef ARDUINOSHT
#define ARDUINOWIRE
#endif
#undef ARDUINOMQ2
#define MQ2PIN A0
#undef ARDUINOLMS6
#define ARDUINOAHT
#undef ARDUINOBMP280
#undef ARDUINOBME280
#endif

Te czujniki są obecnie obsługiwane, jako wbudowane. Aktywacja ARDUINOSENSORS poprzez ustawienie definicji oraz aktywacja czujnika w tym miejscu spowoduje, że wartości czujnika będą dostępne w języku BASIC. Jeśli czujnik DHT22 jest dostępny i zostanie podłączony do pinu D3, sekcja DHT powyżej zmieni się na:

#define ARDUINODHT
#define DHTTYPE DHT22
#define DHTPIN 3

Następnie, w sekcji definicji sprzętowej dodane powinno zostać:

#define ARDUINOSENSORS

Po oznaczeniu flagi ARDUINOSENSORS pozostaje w tym przypadku tylko pobranie biblioteki dla DHT22 i rekompilowanie szkicu w Arduino IDE. Sensor podłączamy do pinu 3 modułu. Aby odczytać wartość używana jest funkcja SENSOR, tak jak pokazano w kodzie poniżej. Pierwszy argument to numer sensora, a drugi, to numer wartości, jaka ma być odczytana. I w ten sposób program:

PRINT SENSOR(1,2), SENSOR(1,1)

Wydrukuje na ekranie obecną temperaturę i wilgotność. Jeśli potrzebne jest nam coś bardziej skomplikowane, lub chcemy po prostu wysłać te dane do serwera MQTT, możemy skorzystać z kodu:

10 OPEN &9, "iotbasic/testdata", 1
20 PRINT &9, "dhtdata:", SENSOR(1,2), SENSOR(1,1)
30 DELAY 2000
40 GOTO 20

Dzięki czterem linijkom kodu w BASICu dane mogą być mierzone i przesyłane na zdalny serwer. Zapisanie tego pliku, jako autoexec.bas na karcie SD spowoduje jego automatyczne uruchomienie po każdym ponownym uruchomieniu komputera. Dzięki temu komputer może działać, jako samodzielne urządzenie do akwizycji danych.

Podsumowanie

W powyższym artykule opisano ciekawe zastosowanie modułu Raspberry Pi RP2040 z programowym interpreterem BASIC-a. Komputer ten może działać zupełnie samodzielnie, posiadając własną pamięć w postaci układu EEPROM, czy też karty SD, czy nawet podłączenie do Internetu! BASIC nie jest może najczęściej używanym współcześnie językiem, jednak sam projekt pozwala zapoznać się z tym łatwym w nauczeniu i używaniu językiem programowania. Interpreter BASIC-a ma wiele funkcji, które nie zostały omówione w tym artykule.

Może działać, jako urządzenie podrzędne, a także odbierać i wyświetlać komunikaty z serwera MQTT. Istnieje cały szereg modułów, których tutaj nie opisano, z uwagi na ograniczoną objętość artykułu.

Nikodem Czechowski, EP

Źródła:

Artykuł ukazał się w
Elektronika Praktyczna
październik 2022

Elektronika Praktyczna Plus lipiec - grudzień 2012

Elektronika Praktyczna Plus

Monograficzne wydania specjalne

Elektronik kwiecień 2024

Elektronik

Magazyn elektroniki profesjonalnej

Raspberry Pi 2015

Raspberry Pi

Wykorzystaj wszystkie możliwości wyjątkowego minikomputera

Świat Radio maj - czerwiec 2024

Świat Radio

Magazyn krótkofalowców i amatorów CB

Automatyka, Podzespoły, Aplikacje kwiecień 2024

Automatyka, Podzespoły, Aplikacje

Technika i rynek systemów automatyki

Elektronika Praktyczna kwiecień 2024

Elektronika Praktyczna

Międzynarodowy magazyn elektroników konstruktorów

Elektronika dla Wszystkich maj 2024

Elektronika dla Wszystkich

Interesująca elektronika dla pasjonatów