[BAS] Ein kleines X11-Basic Tutorial

  • Interessiert Dich das Thema so sehr, dass ich eine Fortsetzung schreiben soll? 10

    1. Ja unbedingt (6) 60%
    2. Nein danke (2) 20%
    3. Um Gottes Willen, bloß nicht. (1) 10%
    4. Naja, wenns sein muß. (1) 10%

    Ich weiß, ich bin einer der wenigen, die immer noch BASIC als Programmiersprache nutzen. Das machen nicht mehr viele, obwohl die Sprache recht populär war. Heutzutage hat python das Rennen gemacht und ist in die Niesche gesprungen, die einst BASIC hatte. Aber was soll ich sagen, BASIC ist noch nicht tot. Es gibt sogar (mindestens) eine Version auch für den Raspberry Pi. Ich habe mich für X11-Basic entschieden, da ich früher mal in GFA-BASIC programmiert hatte. Und X11-Basic kommt dem GFA-Basic (sowie dem TURBO BASIC XL, für die, denen das noch was sagt.) recht nahe. Das Tolle ist, es gibt ein Paket für den Raspberry Pi, welches man leicht installieren kann, und dann ist die Programmierung von kleinen Programmen zum Messen und zur Datenauswertung ein Kinderspiel.

    Wer das mal ausprobieren will, der möge dieser Anleitung folgen.

    1. Was ist X11-Basic?

    Hier ist die Homepage mit allem Wissenswerten: http://x11-basic.sourceforge.net/

    2. Wie bin ich drauf gekommen, das auf dem Raspberry zu verwenden? Siehe: http://scruss.com/blog/2013/03/0…n-raspberry-pi/

    3. Wie installiere ich das? Am besten ein .deb Paket installieren. Das gibt es hier: https://sourceforge.net/projects/x11-b…asic-Raspberry/

    oder hier (unter dem Reiter "releases") https://codeberg.org/kollo/X11Basic . Bitte möglichst die neueste Version runterladen. (Beim Runterladen kommt bei sourceforge dummerweise seit neuestem zunächst eine Aufforderung, einen Newsletter zu abbonnieren und es tut sich scheinbar nix. Aber das kann man getrost ignorieren. Nach 10 Sekunden Zwangspause sollte der download automatisch starten, wenn nicht, kann man noch "problems downloading" anklicken, dann "direct link", und dann gehts irgendwann. Wegen dieses Blödsinns ist es vielleicht besser, die Version von codeberg runterzuladen. Das ist werbefrei und auch sonst einfach besser.)

    Jetzt erst einmal noch einige Pakete installieren, die X11-Basic braucht:

    Code
    sudo apt-get install wiringpi libasound2 libreadline6  liblapack3  libgmp10  fftw2

    Dann die .deb Datei mit

    Code
    sudo dpkg -i ./x11basic_1.27-58_armhf.deb


    installieren.

    4. Wie benutze ich das? Eigentlich genauso wie python. Man ruft ein BASIC-Programm auf mit:

    Code
    xbasic meinprogram.bas

    Erstellt also eine Datei meinprogram.bas z.B. mit

    Code
    nano meinprogram.bas

    (nano ist mein lieblings-editor, aber Ihr könnt auch irgendeinen anderen nehmen, z.B. pico, nedit, gedit, emacs, vi, ...) und schreibt da rein

    Zitat

    PRINT "Hallo Welt"

    QUIT

    Dann abspeichern und ausführen mit

    Code
    xbasic meinprogram.bas

    Das ist schon alles.

    Wenn Euch das interessiert, erläutere ich in weiteren Beiträgen, wie man einzelne GPIOs ausliest und setzt, oder einfache Berechnungen anstellt.

    Auf der Homepage gibt es auch tausend Beispielprogramme (allerdings keine, die die GPIOs des Raspberry ansprechen). Dafür aber alles mögliche andere. Das ist aber eher was für Fortgeschrittene. Ein Handbuch gibt es auch, sogar auf deutsch (!): http://x11-basic.sourceforge.net/x11basic-de.html

    Die Befehlsreferenz gibt es im (englischen) .pdf Handbuch: http://x11-basic.sourceforge.net/X11-Basic-manual.pdf

    Noch einige Schmankerl vorweg an dieser Stelle: X11-Basic gibt es auch als App für Android Geräte.

    3 Mal editiert, zuletzt von wend (7. April 2021 um 16:33) aus folgendem Grund: Einige Tippfehler korrigiert. Link für den Download aktualisiert, neueste Version.

  • OK, die Umfrageergebnisse sind bislang noch eher uneindeutig. Fangen wir also mit ein paar leichten Themen an:

    Basic Programm ordnungsgemäß beenden:

    Am ende eines Jeden Basic Programms sollte immer der Befehl

    Zitat

    QUIT

    stehen. Nur dann wird auch der Interpreter selbst beendet, welcher ansonsten (z.B. nach END) in den Interaktiven Modus wechselt und sich nicht beendet. Das ist wichtig, wenn man das in cron-jobs verwenden will.

    Sollte man aus Versehen im Interaktiven Modus landen, kann man QUIT einfach eingeben, oder nur "q" oder auch mit CTRL-c gelangt man wieder in die normale Shell.

    BASIC Programm ausführbar machen:

    Wenn man in die erste Zeile eines jeden .bas files (z.B. my.bas)

    Zitat

    #!/usr/bin/xbasic

    schreibt, und dann die Datei ausführbar macht mit

    Code
    chmod 755 my.bas

    Dann kann man das Programm einfach durch Eingabe von

    Code
    ./my.bas

    starten.

    Einmal editiert, zuletzt von wend (7. April 2021 um 16:37) aus folgendem Grund: Zitat --> Codeblock

  • Jetzt ist es für den Raspi-Besitzer natürlich extrem wichtig zu wissen, wie man die GPIOs abfragen und bedienen kann.

    Verfügbarkeit testen:

    Zitat

    PRINT GPIO?

    Mit diesem Programm findet man heraus, wieviele GPIOs ansprechbar sind. Bei mir wird 32 ausgegeben (Ich bin eigentlich sicher, dass mein Raspberry Pi weniger GPIOs hat, aber was solls.).

    Sollte 0 ausgegeben werden, so läuft das Programm entweder nicht auf einem Raspbery pi (sondern auf einem normalen Linux, auf WINDOWS, ANDROID etc...) oder irgendwas ist bei der Installation von X11-Basic nicht so gelaufen, wie es sollte. In letzterem Fall ladet bitte nochmal die letzte Version runter, und installiert diese.

    GPIO Pin Belegung:

    X11-Basic verwendet für die Nummerierung der GPIOs nicht die übliche Nummer, sondern die sogenannte wiring-pi Nummerierung (Wpi). Die hier verlinkte Grafik zeigt die Übersetzung:

    https://raw.githubusercontent.com/ppelleti/hs-wi…pin-diagram.png

    Wiring Pi (und X11-Basic GPIO) Nummer 7 ist also GPIO4. Nummer 0 ist GPIO17 usw. Das ist verwirrend, kommt aber auch anderswo vor.

    LED blinken lassen:

    Nun nehmt eine (low power) LED und einen Vorwiderstand (ca. 1000 Ohm), und sucht Euch einen GPIO aus, und verschaltet diese LED mit dem Widerstand gegen GND. Mit folgendm Programm bringt man diese zum Blinken:


    Das Programm läßt die LED 50 mal Blinken und beendet sich dann.

    (Hinweis: die meisten GPIOs lassen sich nur als root ansprechen, das Programm muss also ggf. mit "sudo" gestartet werden. Probiert es aus, es kommt ggf. eine Fehlermeldung "permissin denied" oder so.)

    GPIO als EIngang abfragen:

    Schließlich will man auch mal einen Wert des GPIOs als Eingang abfragen. Nichts leichter als das:

    Zitat

    pin=7

    a=GPIO(pin)


    Nun enhält die Variable a entweder 0 oder 1, je nach Pegel am GPIO.

    Das ist eigentlich schon alles, was man wissen muß. Vielleicht noch das:

    Das Kommando GPIO pin,x schaltet den pin auf Ausgang, und die Abfrage a=GPIO(pin) schaltet ihn auf Eingang.

    Bei der Beschaltung sollte man also entsprechende Vorsichtsmaßnahmen einhalten, damit eine Eingangsbeschaltung nicht versehentlich Spannung vom Pin sieht oder umgekehrt. Ich löse das immer so, daß ich auf jeden Fall erstmal einen 1kOhm Widerstand an den Pin hänge und dann erst die LED oder den Schalter.

    So kann durch einen blöden Fahler nicht so schnell was kaputtgehen.

    3 Mal editiert, zuletzt von wend (18. Juni 2018 um 12:47)

  • Das nächste wichtige Thema auf einem Raspberry ist es, z.B. für eine automatische Datennahme, ein X11-Basic Programm regelmäßig als cron-job laufen zu lassen.

    ein Programm als Cron-Job laufen lassen:

    Wann ist das sinnvoll?

    1. Wenn man ein Programm (z.B. zum Daten sammeln und messen) nicht häufiger als einmal pro Minute starten will.


    Bei der Programmierung des (BASIC) Programm muß man folgende Punkte beachten:

    1. Das Programm muß unbedingt(!) mit QUIT beendet werden.

    2. Darauf achten, daß es immer beendet wird, und nicht etwa in eine Endlosschleife gerät.

    3. Wenn das Programm andere Programme aufruft, immer absolute Pfade verwenden.

    Ansonsten gibt es keine Einschränkungen für den Inhalt des Programms. Es versteht sich von selbst, daß man in automatisch im Hintergrund ausgeführten Programmen keine Grafik-Ausgaben machen kann oder auch keine Nutzer-Eingaben abfragen kann. Das Programm sollte sich darauf beschränken, Daten von Sensoren oder vom GPIO zu lesen oder zu setzen, Files zu öffnen, daraus zu lesen oder darin zu schreiben, Berechnungen zu machen etc...

    Also "fire and forget", man startet es, dann macht es irgendwas, und es beendet sich danach.

    Schließlich muss man noch eine Kleinigkeit zur "crontab" wissen:

    Die Crontab ist eine Tabelle mit eine Liste von zu bestimmten Zeitpunkten zu startenden Programmen. Sie wird zentral im System gespeichert. Jeder User (also insbesondere der User "pi") hat eine eigene Tabelle. Der User "root" hat eine besondere eigene Tabelle (die ich aber nie verwende).

    Um die Tabelle (des Users pi) zu verändern, muß man in drei Schritten vorgehen:

    1. Aktuelle Tabelle aus dem System lesen und in ein File speichern,

    2. das File mit der Tabelle editieren,

    3. das geänderte File mir der geänderten Tabelle wieder ins System schreiben.

    Das machen die folgenden Befehle (im Terminalfenster, shell):

    Code
    crontab -l > crontab-l

    Schreibt die bisherige Tabelle in das File "crontab-l" im aktuellen Verzeichnis.

    Code
    nano crontab-l

    Ruft den Editor (nano) auf, mit dem man das File bearbeiten kann.

    Code
    crontab crontab-l

    Schreibt das File wieder ins System. Ab nun werden die Programme in der Tabelle bei den angegebenen Uhrzeiten gestartet. Das File "crontab-l" im aktuellen Verzeichnis kann nun wieder gelöscht werden.

    Wie sieht jetzt eine typische crontab-Zeile mit einem X11-Basic Programmm aus:

    Zitat

    15 3 * * * /home/pi/gaszaehler/bas/Gas-Auswertung.bas 2>&1 > /dev/null

    Man beachte das Format, alle Leerzeichen etc. Diese Zeile führt ein X11-Basic Programm jeden Morgen um 3 Uhr 15 aus. In der Datei "Gas-Auswertung.bas" wurde die she-bang-Zeile (s.o.) #!/usr/bin/xbasic als erste Zeile verwendet und sie wurde vorher mit chmod 755 Gas-Auswertung.bas ausführbar gemacht. Man beachte auch, daß ein absoluter Pfad verwendet wurde (Das Programm liegt im Unterverzeichnis: /home/pi/gaszaehler/bas).

    Schließlich wollen wir nicht, daß versehentliche Ausgaben des Programms als Fehlermeldungen per email an irgendwen gesendet werden, deshalb verwenden wir die Ausgabeumleitungen: 2>&1 > /dev/null, die dafür sorgen, daß alle Ausgaben von X11-Basic (mit PRINT oder als Fehlermeldungen) ignoriert werden.

    Hier noch ein anderes Beispiel:

    Zitat

    * * * * * /home/pi/bas/read_pressure.bas 2>&1 > /dev/null

    Ruft jede Minute ein Programm auf, welches den Luftdrucksensor ausliest und die Daten verarbeitet.

    Noch ein praktische Hinweis:

    Man sollte alle Programme, welche man in die crontab eintragen will, vorher ausgiebig testen, in dem man sie "von Hand" aufruft, und schaut, ob das gewünschte Ergebnis erzielt wird.

    Was tun mit root-Rechten?


    Mas macht man nun, wenn ein Sensor (oder die GPIOs) nur mit root Rechten gelesen werden können, also wenn das X11-Basic Programm mit "sudo" gestartet werden muß?

    Nun, hier gibt es verschiedene Möglichkeiten, die auch hier im Forum schon diskutiert wurden. Ich selbst mach das aber immer so:

    Da ich mich nur um eine crontab kümmern möchte, nämlich um die des Users pi, lasse ich meine X11-Basic cron-jobs nur normal laufen. Wenn die dann irgendwann eine Auslese nur mit root-Rechten machen können, dann rufe ich in X11-Basic (mit SYSTEM "sudo ..."oder SYSTEM$("sudo ...")) ein weiteres X11-Basic progrann, shell skript oder python skript auf, welches dann nur dieses Auslesen macht und den Wert über stdout ausgibt. Dieses dann mit sudo.

    Die weitere Datenverarbeitung geschieht dann im Haupt-cron-job. So behalte ich den Überblick.

    Und hier noch der Code des X11-Basic Programms "read_pressure.bas" aus obigem Beispiel:

    (Die python-Library des Adafruit_BMP085 sensors hatte ich vorher runtergelden.)

    2 Mal editiert, zuletzt von wend (7. April 2021 um 16:41) aus folgendem Grund: zitat --> codeblock

  • Noch ein wichtiges Thema:

    X11-Basic Programm beim Systemneustrart automatisch starten

    Nicht immer kann eine Datennahme über einen regelmäßigen Cron-Job vernünftig geschehen, z.B.:

    * wenn man öfter als einmal pro Minute was tun muß, oder

    * wenn eine Verbindung zu einem Sensor dauerhaft gehalten werden muß.

    In diesem Fall würde man ein X11-Basic Programm schreiben, welches irgendwas in einer Endlosschleife macht. Dieses soll dann automatisch beim Neustart des Raspberry Pis gestartet werden.

    Auch hier gibt es viele verschiedene Möglichkeiten. Ich selbst mach das über die Crontab und verwende zusätzlich aber noch das praktische Tool "screen".

    Wie man die crontab ändert, hab ich ja oben schon geschrieben. Um ein Programm beim Neustart einmal zu starten, muß man dort in der ersten Zeile folgendes eintragen:

    Zitat

    @reboot /home/pi/box/Feinstaub/bas/start-feinstaub.bas 2>&1 > /dev/null


    Im Prinzip ähnlich wie bei den regelmäßigen Jobs.

    Nun schauen wir uns "start-feinstaub.bas" mal an:


    start-feinstaub.bas startet also nur eine anderes Programm (feinstaub.bas), was dann das Eigentliche macht. Der Witz ist hier, daß ich eine "screen" Umgebung verwende. Kurz gesagt macht diese folgendes: Sie erstellt ein virtuelles (und zunächst unsichtbares) Terminal im Hintergrund in das auch alle Ausgaben (PRINT etc..) geschrieben werden.

    Dort läuft also nun das Datennahmeprogramm feinstaub.bas. Soweit nix neues, aber wenn ich mich nun in den Raspberry Pi einlogge und sehen will, was das Programm gerade macht, kann ich mir das virtuelle Terminal nach vorne holen mit

    Code
    screen -r

    (Wobei man hinter -r noch eine Nummer angeben muss, wenn es mehrere virtuelle screens zur Auswahl gibt).

    Man kann dann schauen, was so vor sich geht, das Programm auch mit CTRL-c abbrechen und dann ggf. ändern und neustarten, aber vor allem kann man mit CTRL-a, dann CTRL-d dieses virtuelle Terminal wieder verlassen, ohne das dort laufende Programm abzuwürgen. Man schickt es so wieder samt Ausgaben in den Hintergrund.

    Das Programm "feinstaub.bas" öffnet übrigens dauerhaft /dev/ttyUSB0 zu einem dort abgeschlossenen SDS011 Feinstaubsensor und wartet auf Daten, liest diese, schreibt diese in eine Datei und wartet auf die nächsten Daten, endlos usw....

    Ach so, screen muß noch installiert werden:

    Code
    sudo apt-get install screen

    2 Mal editiert, zuletzt von wend (7. April 2021 um 16:43) aus folgendem Grund: zitat --> codeblock

  • Vielleicht kannst du anstatt Zitat, Code Tags benutzen (für Einzeiler Inline Code?)? Wäre mMn besser lesbar. Es sei denn es gibt einen bestimmten Grund für Zitat.

  • Ein häufiges Thema ist auch:

    Daten speichern

    Wie speichert man am besten Daten auf der Fetplatte (bzw. SD-Karte)?

    Hier gibt es auch wieder viele Möglichkeiten. Z.B. empfehlen Leute immer wieder die Verwendung von Datenbanken (MYSQL et al.) . Das ist für sehr große Datenmengen, in denen viel gesucht werden muß, recht praktisch. Ich selbst habe aber kaum Ahnung davon, insbesondere weil man MySql wie eine eigene Programmiersprache auch noch lernen und verstehen muß.

    Ich bin schon älteres Semester und verzichte gerne darauf. Nachteil: Beim Suchen in den Daten wird es dann schwieriger.

    Aus X11-Basic ist es ein Leichtes, einfach eine Datei zu öffnen und die Daten in ASCII, also lesbar, zeilenweise, meinetwegen mit Komma getrennt, da reinzuschreiben. Genausoleicht kann man die Datei wieder öffnen und zeilenweise einlesen, wobei man dann natürlich alle Daten durchgehen muß, wenn man was sucht.

    Auch wenn viele gerne Kommata zum Trennen einzelner Datensätze verwenden (in sogenannten .CSV Dateien), habe ich mir eine andere Konvention angewöhnt:

    Ich schreibe immer einen Datensatz pro Zeile, die einzelnen Werte innerhalb einer Zeile werden mit einem oder mehrern Leerzeichen getrennt, wenn Leerzeichen in einem Datum vorkommen, umschließe ich sie mit Anführungszeichen. (ENCLOSE$())

    Ist die ganze Zeile kein Datensatz sondern ein Kommentar order dergleichen, lasse ich sie mit einem "#" oder "%" beginnen.

    Als weitere Konvention habe ich mir angewöhnt, UNIX-Zeitstempel zu verwenden anstelle von Datum und Uhrzeit. (Damit ist Sommerzeit/Winterzeit kein Problem). Außerdem ist es praktisch, eine Datei pro Tag anzulegen, und dafür die Datei dann nach einer Tagesnummer zu benennen. Das geht zum Beispiel mit dem Julianischen Datum (Anzahl der Tage seit den alten Ägyptern (oder besser seit Adam und Eva)), oder auch einfacher mit dem aus dem UNIX-Zeitstempel berechneten Tagesanzahl seit Jan 1970.

    Eine typische Datei sieht dann etwa so aus (feinstaubstat.dat):

    Code: feinstaubstat.dat
    % Tagnr Datum      pm25                                 pm10
    17686 04.06.2018    2.3    3.573    6.3    2.5   3.8    2.5    4.104   13.0    2.8   4.2
    17687 05.06.2018    2.0    3.316    4.6    4.0   2.9    3.3    6.924   16.0    4.4   4.5
    17688 06.06.2018    0.9    1.785    3.0    2.8   1.6    1.2    3.138   12.5    4.6   2.5
    17689 07.06.2018    1.0    2.051    3.7    1.4   2.3    1.1    3.564    9.1    2.0   4.0
    17690 08.06.2018    1.6    3.046    4.8    2.3   3.0    2.0    4.748    8.6    3.5   4.3
    17691 09.06.2018    1.2    2.221    3.6    3.1   1.4    1.2    2.891    9.3    4.3   1.5
    17692 10.06.2018    1.1    1.543    3.3    1.4   2.4    1.1    1.821   10.4    1.5   2.6
    17693 11.06.2018    1.8    2.283    5.2    2.2   1.8    2.0    4.605   16.8    2.4   3.7
    17694 12.06.2018    0.8    1.667    3.1    1.7   1.9    0.9    4.019   16.5    3.3   3.3


    oder auch so (feinstaubdata-17700.dat)

    Man kann die Dateien leicht mit jeden Editor lesen (und auch modifizieren) oder auch mit anderen UNIX-Tools weiterverarbeiten.

    Und so werden die erzeugt:

    Die Prozeduren waitdata und readdata hab ich jetzt mal weggelassen, um den Rest zu erläutern. Es handelt sich um eine doppelte Endlosschleife, damit jeden Tag ein neues File angelegt wird. In der inneren Schleife werden die Daten des Sensors gelesen und in die Datei geschrieben.

    Warum Old-School?

    Ok, das alles ist ziemlich "old school", also Programmierstil der 80er Jahre des letzten Jahrhunderts.

    Warum soll man das heute noch so machen?

    Tja, wenn einer BASIC verwendet, dann kommt es darauf auch nicht mehr an.

    Oder auch:

    Weil man dann mit einem weiteren (uralten) Tool namens "gnuplot" kinderleicht aus den gespeicherten Daten Grafiken machen kann (die man dann genauso kinderleicht auf einer WEB-Seite anzeigen lassen kann).

    Hier bräuchte man jetzt eigentlich noch ein gnuplot Tutorial, aber wer neugierig ist:

    Hier mein gnuplot file (feinstaub.gnu), was genau die oben gespeicherten Daten verarbeitet und einen netten Plot für die WEB-Seite herstellt, die das ganze dann anzeigt:

    Code: feistaub.gnu
    set term png nocrop enh  size 1024,400
    set output "/var/www/feinstaub.png"
    set gridset title "Aktueller Feinstaub 18.06.2018"
    set xlabel "Stunde"
    set xtics 1
    set ylabel "µg/m³"
    plot [0:24][:] "/home/pi/box/Feinstaub/data/feinstaubdata-17700.dat" u ($1/3600):2 w st t "PM 2.5", \
    "" u ($1/3600):3 w st t "PM 10"


    (Als WEB-Server hab ich apache installiert, eine index.html Datei wird nachts von einem cron-job geschrieben, gnuplot wird von cron jede Stunde aufgerufen, um /var/www/feinstaub.png zu aktualisieren.) Und fertig ist die vollständige Datenvisualisierung.

    gnuplot installieren:

    Bash
    sudo apt-get install gnuplot

    apache WEB server installieren:

    Bash
    sudo apt-get install apache

    Mehr Aufwand will ich eigentlich mit meinen Sensoren gar nicht treiben, vor allem muß ich so nicht noch zig andere Programmierkonzepte, Datenbankkonzepte, Visualisierungslibraries und die zugehörigen Datenformate oder APIs lernen. Als Kit, der alles zusammenhält, verwende ich dann das Basic.

    Damit mache ich dann z.B.:

    * Tägliche Zusammenfassung der Daten (Tagesstatistiken),

    * Automatische Emails mit Daten und Plots (an mich selbst) versenden,

    * Nach einigen Tagen, die Rohdaten mit gzip zu größeren Paketen zusammenfassen und archivieren.

    * ...

    Noch ein Hinweis:

    Es wird ja öfters argumentiert, daß zu häufiges kleinteiliges Schreiben auf die SD-Karte deren Lebensdauer verringert. Um dem etwas vorzubeugen, schreibe ich kleinere Zwischenergebnisse auf die Ramdisk (/run/shm/). Außerdem muß man nicht nach jedem PRINT #1 in eine Datei diese schließen und wieder öffnen. Besser ist es, diese geöffnet zu lassen, dann schreibt das Betriebsystem erst, wenn 4096 Bytes zusammengekommen sind und ein vollständiges Paket geschrieben werden kann. Möchte man explizite Syncs mit dem Dateisystem, nimm den Befehl FLUSH #1. (Damit z.B eine aufgerufenes gnuplot keine halben Zeilen im Datenfile vorfindet.)

    Es ist außerdem besser, immer hinten an das Dateiende anzufügen, als Daten innerhalb der Datei hin und herzuschieben.

    Auf diese Weise sind mir in 5 Jahren Dauerbetrieb noch keine SD-Karten kaputtgegangen.

    3 Mal editiert, zuletzt von wend (19. Juni 2018 um 11:06)

  • Hallo wend,

    ich finde die Idee für Dein Tutorial gut!

    Allerdings würde ich noch hergehen und im ersten Beitrag ein Inhaltsverzeichnis (mit Links auf die einzelnen Beiträge) erstellen. Dann ist das füe einen Neuen leicher, sich zurechtzufinden. Denn so wird's langsam unübersichtlich...

    Vielleicht kann ein Forenadmin auch einen neuen/leeren Beitrag mit deiner Kennung ganz vorne einfügen?

  • Heute gibt es das Thema

    USB-tty öffnen

    Auch wenn es eigentlich kein so spezifisches BASIC Thema ist, beschreibe ich heute, wie man einen an einer tty Schnittstelle angeschlossen Sensor anspricht und dessen Daten lesen kann.

    Zunächst einmal stellt das Betriebsystem für jedes tty ein eigenes Device bereit. Diese Devices werden im Filesystem im Ordner /dev dargestellt:

    Code
    /dev/tty
    /dev/tty0
    /dev/tty1
    ...

    Wenn man nun ein USB-zu-tty Adapter ansteckt, dann taucht dort das device /dev/ttyUSB0 auf. Wenn man mehrere ansteckt, dann auch /dev/ttyUSB1, /dev/ttyUSB2, ...

    Anhand der file permission dort werden die Zugriffsrechte geregelt. Meist hat nur root und alle user in der gruppe "tty", manchmal auch "dialout" Lese- und Schreibrechte.

    Das kann man natürlich ändern:

    Code
    sudo chown pi /dev/ttyUSB0

    Meist bleibt das aber nicht über einen Neustart des Raspberys bestehen. Deshalb kann man das BASIC Programm, was darauf zugreifen soll, auch mit "sudo" starten. Jedoch ist es besser, innerhalb des BASIC Programms einfach

    Code
    SYSTEM "sudo chown pi /dev/ttyUSB0"
    SYSTEM "chmod 644 /dev/ttyUSB0"

    auszuführen, bevor man das Device öffnet.

    geöffnet wird es dann mit

    Code
    OPEN "UX:9600,N,8,1",#11,"/dev/ttyUSB0"
    ' Buffer leeren
    WHILE INP?(#11)
      ~INP(#11)
    WEND
    PAUSE 0.2

    Hierbei erkennt man schon die EInstellungen 9600 Baud, 8 bit, 1 Stopbit, keine Parität. Die Kanalnummer (11) ist willkürlich gewählt. Die kleine Schleife leert den Empfangsbuffer, falls schon Daten anliegen.

    Nun kann man mit allen File-Operationen (INPUT$(), PRINT #, etc.) auf die Serielle Schnittstelle schreiben und lesen.

    Einige Hinweise:

    * INP?(#11) liefert 0 wenn kein Zeichen empfangbar ist.

    * a=INP(#11) liest genau 1 Byte

    * PRINT #11,"Hallo"; schreibt die Zeichenkette "Hallo" in den Kanal (Ohne Zeilenendezeichen)

    * FLUSH #11 Sorgt dafür, dass alles im Buffer auch wirklich rausgesendet wird.

    * a$=INPUT$(#11,5) liest genau 5 Bytes

    * LINEINPUT #11,t$ liest eine ganze Zeile (bis Zeilenendezeichen)

    Beim Lesen muss man wissen, dass das Porgramm solange wartet, bis wirklich die angegebene Anzahl bytes empfangen wurde, oder ein Zeilenendezeichen gelesen ist (bei LINEINPUT). Will man das nicht, dann muss man Byteweise einlesen und jedesmal fragen, ob (noch/schon) ein neues Byte zu lesen ist.

    Hier ein Beispiel: Der SDS011 Feinstabsensor sendet Binaerdaten in Paketen von 10 Bytes, wobei das erste Byte immer den Wert 170 haben muss:

    Code
    ...
    DO
      EXIT IF t<>INT(TIMER/3600/24)
      ' Synchronisiere mit Anfang des Blocks
      WHILE INP(#11)<>170
      WEND
      t$=INPUT$(#11,9)
      @processmessage(t$)
    LOOP
    ...


    Die Procedure processmessage verarbeitet dann die empfangenen Daten.

    Genauso kann man Binardaten zum Sensor hinschicken (nachdem man ein gültiges Datenpaket geformt hat):

    Am Ende der Komunikation sollte der Kanal zum Sensor geschlossen werden (Bei Endlosschleife natuerlich nicht nötig):

    Code
    CLOSE #11

    Das ist eigentlich schon alles und geht genauso mit anderen an tty angeschlossenen Geräten. Wenn kein ASCII-Protokoll verwendet wird, ist es natuerlich aufwendig, dies in BASIC dann umzusetzen, aber es geht und ist -- wie ich finde -- recht durchsichtig. Un da die Sensoren für den Raspberry Pi ja recht einfach sind, ist es noch vertretbar, die "Treiber" schnell selbst zu schreiben. Z.B. verwenden Multimeter ein einfaches ASCII Protokoll: Man sendet ein Zeichen, z.B. "D", das Multimeter antwortet dann mit einer Zeile ASCII mit dem Meßwert.

    Kommuniziert man mit einem Arduino, so muss man das Protokoll sowieso selbst definieren.

    3 Mal editiert, zuletzt von wend (19. Juni 2018 um 10:04)

  • Anhand der file permission dort werden die Zugriffsrechte geregelt. Meist hat nur root und alle user in der gruppe "tty", manchmal auch "dialout" Lese- und Schreibrechte.

    Das kann man natürlich ändern:

    Kann man, aber besser über eine entsprechende udev-rule oder indem man einzelne User in die passende Gruppe legt. chown ist imho hier der falsche Weg, egal, ob persistent oder nicht.

    Wenn du nichts zu sagen hast, sag einfach nichts.

  • Noch ein Thema:

    Raspberry Pi aus der Ferne mit einem WEB-Browser steuern

    Will man auf dem Raspberry einige Funktionen steuern, so benötigt man ein Nutzerinterface. Hierfür gibt es viele Möglichkeiten, jedoch wollen wir was einfaches. Ich mach das über einen WEB-Browser, wo ich ein CGI-Script aufrufe, was dann bestimmte Aktionen auf dem Pi ausführt. Das CGI-Script kann auch ein X11-Basic Programm sein.

    Zunächst muß man einen WEB-Server installieren:

    Bash
    sudo apt-get install apache

    Dann gibt es ein Verzeichnis für normale WEB-Seiten (html): meist /var/www oder /var/www/html und ein anderes Verzeichnis für cgi-scripts: /usr/lib/cgi-bin

    Wo die Verzeichnisse liegen, varriiiert leider etwas von apache-Version zu Version. Die Konfiguration von apache liegt in /etc/apache/ und diversen Unterordnern. Irgendwo dort ist auch definiert, wie die Verzeichnisse heißen.

    Ein BASIC-cgi-script muß im Ordner /usr/lib/cgi-bin liegen, ausführbar sein (chmod 755 my.cgi) und dem User "pi" gehören.

    Weiterhin muß es sich zwinged mit "QUIT" beenden und als erstes folgende Zeilen haben:

    Code: my.cgi
    #!/usr/bin/xbasic
    PRINT "Content-type: text/html"+CHR$(13)
    PRINT ""+CHR$(13)
    FLUSH
    ...

    Danach muss es mit "PRINT" irgendwas in html ausgeben, also z.B.


    Die Seite kann man dann im Browser über die folgende URL aufrufen:

    Zitat

    Und dann erscheint die vom Script erzeugte Seite. Natürlich kann man das cgi-script auch über ein z.B.

    Zitat

    <a href='/cgi-bin/action.cgi?plot'><img src='Leistung.png'></a>

    auch in einere statischen WEB-Seite (z.B. index.html) einbinden.


    Der Witz ist nun, dass man dem CGI-Script über den Browser auch einen oder mehrere Parameter übergeben kann:

    Zitat

    Im script kommt man an den Parameter dan so ran:

    Code
    t$=ENV$("REQUEST_URI")
    SPLIT t$,"?",1,a$,t$
    SPLIT t$,"=",1,par$,wert$
    ...

    In par$ ist dann der Parametername und in wert$ der zugewiesene Wert.


    Also kann man z.B. folgendes machen:

    Zitat

    und im Script entsprechend reagieren.

    Damit man nicht immer URLs von Hand eintippen muss, hat html das schöne Feature, Formulare mit Buttons etc. zu kreieren, die dann die Seite aufrufen. Auf diese Weise kann man sich ein passables Nutzerinterface zusammenbauen. Damit das hier nicht ausartet, möchte ich auf die Beispielprogramme für X11-Basic verweisen, die sich mit dem Thema befassen. Von dort hab ich einfach abgeguckt.

    Hier der Link: https://codeberg.org/kollo/X11-Basi…/master/All/CGI

    3 Mal editiert, zuletzt von wend (7. April 2021 um 16:49) aus folgendem Grund: repaired broken link.

  • müssen die Anführungszeichen im String nicht escaped werden, da du die ja auch schon für den String selbst verwendest?

    Ja, stimmt. Hab ich noch gar nicht gemerkt, das Script hat auch so funktioniert.... Ich aender das mal. (Ansonsten: nix bei mir ist zeitgemäß. Ich bin zeitlos(!))

  • Mir fällt gerade noch ein Thema ein:

    TIPP: CPU Auslastung

    Einen gewissen Augenmerk sollte man auch auf die CPU-Auslastung eines BASIC Programms legen. Insbesondere, wenn man mehere Scripte / Aufgaben gleichzeitig vom Raspberry erledigen lassen möchte (also immer).

    Nehmen wir an, das Programm erfordert eine Endlosschleife

    Code
    DO
      IF INP?(#1)
        @lese_daten
      ENDIF
    LOOP

    Dann funktioniert das zwar, aber was ist wenn INP?(#1) 0 liefert? Dann wird der Raspberry Pi mit irrer Geschwindigkeit immer wieder nur INP?(#1) ausführen. Das führt zu einer CPU-Auslastung von 100% und ist aber unnötig.

    Meist ist es genug, nur alle 10 Millisekunden nach Daten zu fragen. Fügt man nur eine (winzige) PAUSE von 0.01 s ein, so sinkt die CPU-Auslastung bereits drastisch. Nimmt man 0.1 s, ist der Prozeß in der TOP10 der Prozessorbelastung schon kaum noch sichtbar. An der Funktion des BASIC Programms hat sich aber nichts geändert.

    Code
    DO
      IF INP?(#1)
        @lese_daten
      ENDIF
      PAUSE 0.1
    LOOP

    Soweit so gut. In obigem Beispiel macht das BASIC Programm aber eigentlich gar nichts, wenn keine Daten anliegen, also kann man es noch weiter optimieren:

    Code
    DO
        @lese_daten
    LOOP
    ...
    PROCEDURE lese_daten
       a=INP(#1)
      ...
    RETURN

    In diesem Beispiel geht die CPU-Auslastung ebenfalls nicht hoch, da das Lesen der Daten mit INP(#1) grundsätzlich wartet, wenn keine Daten verfügbar sind. Und in der Wartezeit wird die CPU für andere Aufgaben freigegeben.

    Nur das BASIC Script selbst kann in der Wartezeit nix anderes machen.

    Man sollte also etwas überlegen, wie man die Dateneingabe über ein File oder Serielle Leitung gestaltet.

  • Ich hätte noch folgende Themen im Angebot:

    * BASIC Programme schneller laufen lassen

    * Grafik in der Console (ohne X !)

    * Buffer und Speichermanipulationen mit Strings

    * Binäre Daten entschlüsseln und manipulieren

  • Heute liefere ich noch folgendes Kapitel nach:

    BASIC Programme schneller laufen lassen

    Wie soll das gehen? Nun, X11-Basic ist nicht nur ein Interpreter (also nimmt die Text-Datei .bas und führt sie Zeile für Zeile aus), sondern man kann die BASIC Programme auch Kompilieren.

    Das macht man so

    Code
    xbbc meinprogramm.bas

    Anschliessend findet Ihr eine (nicht sehr aussagekräftige Datei namens "b.b" im gleichen Verzeichnis. Und Oh wunder, diese Datei läßt sich auch ausführen:

    Code
     xbasic b.b

    Und vielleicht habt Ihr es gemerkt: Es geht gleich 10 bis 20 mal schneller.

    Nun, b.b ist blöd, also nehmen wir:

    Code
    xbbc -o meinprogramm.b meinprogramm.bas

    Jetzt heisst das Ergebnis "meinprogramm.b". Die Datei ist eine Binaerdatei und kann nicht mehr im Texteditor bearbeitet werden. (das Kompilieren hat seinen Preis). Aber das kann ja auch manchmal vorteilhaft sein, und auf jeden Fall läuft das Programm (viel) schneller.

    Zur Info: Es ist ein Maschinencode für eine virtuelle Maschine, also noch nicht richtig für den Prozessor (arm) kompiliert aber immerhin schon einen Schritt näher dran. Java macht das übrigens auch so.

    Leider kann man "meinprogramm.b" jetzt so nicht direkt ausführen, wie oben schon mit der she-bang Zeile. Aber dafür gibt es einen Trick:

    Legt einfach eine Datei namens "schnellbasic" mit folgendem Inhalt an:

    Und macht diese ausführbar (chmod 755 schnellbasic) und verschiebt sie am besten in /usr/bin/ (sudo mv schnellbasic /usr/bin/ ).

    Und jetzt kann man in ein normales .bas File als erste Zeile folgendes eintragen:

    Code
    #!/usr/bin/schnellbasic

    (oder es auch anstelle mit xbasic mir schnellbasic aufrufen) und schwuppdiwupp läuft das Programm (scheinbar ohne vorherige Kompilierung) schneller. Wie kann das sein? Nun durch den Trick wird das Programm in zwei Schritten zuerst kompiliert und dann das kompilierte Programm gestartet,

    So einfach kann es sein...! Man hat nun die vorteile der Schnelleren Ausführung kombiniert mit dem einfachen Editieren des Files.

    Für Fortgeschrittene: Man kann auch echte (native) ausführbare Maschinenprogramme daraus machen. Dafür gibt es den Kompiler "xbc". Aber das soll jetzt nicht Thema dieses Tutorials sein... Der zusätzliche Geschwindigkeitsgewinn ist nicht mehr so dramatisch.

    Da sagt bitte niemand mehr, BASIC sei alt und lahm. ich würde jetzt mal behaupten, auf diese Weise laufen BASIC programme sogar schneller als vergleichbare python Skripte. (Aber ich habe das jetzt nicht wirklich überprüft.) Und das kann ja bei insbesondere älteren Raspberry Pi Modellen stark von Vorteil sein.

    Einmal editiert, zuletzt von wend (7. April 2021 um 17:17)

Jetzt mitmachen!

Du hast noch kein Benutzerkonto auf unserer Seite? Registriere dich kostenlos und nimm an unserer Community teil!