Beim stöbern auf AliExpress bin ich auf diesen ISP Programmer gestossen. Der Preis ist wirklich attraktiv und da dachte ich mir, dieser Programmer wäre eine schöne Erweiterung für meine Projekt-Boxen aus der MakerAG. Gesagt, getan, habe ich ein paar davon bestellt.
Die Programmer kommen in verschiedenen Farben. Das Alugehäuse ist brauchbar verarbeitet und es ist ein entsprechendes Flachbandkabel beigelegt. Das Produkt kommt in einer antistatischen Tüte. Die Lieferzeit bei meiner Bestellung betrug ca. 4 Wochen.
Die Ernüchterung trat beim ersten Test unter Ubuntu 20.04 ein. Die LED am Gerät leuchtet rot. Das Gerät wird als USBHID erkannt. Manufacturer: zhifengsoft. Ein solches Produkt kennt weder der Linux-Kernel, noch kann Arduino es ansprechen. Die USB-ID ist 03eb:c8b4 und wird im Kommando lsusb zwar als Atmel Corp. ausgewiesen, aber eine Funktion konnte ich dem Stick nicht entlocken.
[68880.160613] usb 1-1.5: new low-speed USB device number 9 using ehci-pci [68880.276311] usb 1-1.5: New USB device found, idVendor=03eb, idProduct=c8b4, bcdDevice= 1.00 [68880.276318] usb 1-1.5: New USB device strings: Mfr=1, Product=2, SerialNumber=0 [68880.276322] usb 1-1.5: Product: USBHID [68880.276325] usb 1-1.5: Manufacturer: zhifengsoft
Bus 001 Device 009: ID 03eb:c8b4 Atmel Corp.
Einige Recherchen im Netz (z.B. hier und hier) haben eine Lösung zu Tage gefördert, die nicht nur die Funktion sicher stellt, sondern auch auf die frei verfügbare Softwarelösung USBasp von Thomas Fischl setzt. Da die vorstehend genannten Links relativ alt sind, arbeite ich sie hier noch einmal auf.
Hardware
Das Gehäuse des Programmers lässt sich öffnen, indem man einfach das Alugehäuse in Richtung des USB Stecker abzieht. Keine Scheu, es geht nicht allzu schwer. Bei einem Stückpreis von knapp 2 Euro wäre der Verlust aber auch zu verschmerzen 😀
Nach dem Öffnen kommt, zumindestens bei mir, eine relativ sauber gearbeitete Platine zum Vorschein.
Dort finden wir einen Atmega88 mit einem 12 Mhz Quarz, sowie einige andere Bauteile. Darell Tan geht in diesem Blog-Post genauer auf den Schaltplan ein. Die für uns relevante Information ist der Atmega88 im TQFP Gehäuse als Basis-Chip. Sowie die Erkenntnis, dass der USB D- PIN zusätzlich zum GPIO PB0 auch noch an den GPIO PD3 des Atmega88 angebunden ist (Der USB D+ PIN an GPIO PB1 und PD2). Das ist ein Unterschied zum USBasp Bauplan von Thomas Fischl und erfordert Anpassungen an seiner Software, damit diese auf dieser Hardware lauffähig wird. Weitere Details zum USB-Port und -Bus finden sich hier.
Um die Software auf dem Atemga88 ersetzten zu können, muss der Programmer in den “Update Mode” versetzt werden. Dazu müssen die beiden Lötpunkte, die mit “–>UP<–” gekennzeichnet sind, mit einer Brücke verbunden werden. Technisch wird dadurch der Reset-PIN des ISP Stecker (oben rechts im Bild) mit dem Reset-PIN des Atmega88 verbunden. Dadurch kann man mit einem zweiten ISP Programmer dem Atmega88 in den Upload-Zustand versetzen.
Die Stromversorgung darf meiner Erfahrung nach nicht via USB erfolgen, wenn der Atmega88 programmiert werden soll. D.h. die Stromversorgung muss über den ISP Stecker auf der rechten Seite erfolgen.
Software
Leider gibt es da ein gewisses Henne-Ei Problem. Wir wollen ja den Atmega88 neu programmieren. Dafür brauchen wir einen ISP Programmer, den wir aber noch nicht haben, weil er ja nicht funktioniert 😐 Aber dafür gibt es eine Lösung: Wenn man bereits ein anderes Arduino Board besitzt. Also z.B. ein Arduino Nano oder ein Arduino Uno, dann kann man dieses Board als Arduino ISP verwenden um den Atmega88 zu programmieren.
Arduino ISP vorbereiten
Der notwendige Sketch wird in der Arduino Umgebung freundlicherweise gleich mitgeliefert. Ich verwende hier im Beispiel einen Arduino Nano Klon.
Als Erstes muss man in der Arduino Oberfläche das entsprechende Board auswählen. Dies erfolgt im Menu “Werkzeuge”, unterer Bereich.
Anschließend aus den Beispielen den “ArduinoISP” Sketch auswählen. Dies erfolgt im Menu “Datei” -> “Beispiele” -> “11.ArduinoISP“
Wenn der Sketch geladen ist, kann er durch einen Klick auf den “Haken” oben Links in der Arduino Oberfläche kompiliert werden. Mit dem “Pfeil” rechts neben dem “Haken” kann der Sketch dann auf den Nano übertragen werden. Erfolgs- oder Fehlermeldungen finden sich im “schwarzen Kasten” unten in der Oberfläche. Der folgende Screenshot zeigt ein erfolgreiches Hochladen (Hochladen abgeschlossen).
Jetzt kann, wie im zu Beginn stehenden Kommentarbereich des Sketch beschrieben, die Verkabelung erfolgen. Ich spare mir die 3 Status-LEDs, da wir auch an der Ausgabe sehen ob es klappt oder nicht. Wichtig ist noch: “Pin 10 is used to reset the target microcontroller”. Die Belegung ergibt sich also wie folgt:
Nano Klon | USB ISP Stecker |
D10 | RESET |
D11 | MOSI |
D12 | MISO |
D13 | SCK |
+5V | VCC |
GND | GND |
Wenn alles passt, dann leuchtet der Nano grün und der USP ISP rot, sobald man den Nano an den USB Port des Rechners hängt. Bitte *IMMER* vor allem die Stromversorgung doppelt prüfen. Wenn da etwas nicht stimmt, kann gerne alles in Rauch aufgehen. Vor allem, wenn es dann den USB Port am Rechner gegrillt hat, ist der Tag ganz sicher gelaufen.
USBasp anpassen und hochladen
Nachdem alle notwendigen Vorarbeiten erledigt sind, kommen wir zur eigentlichen Aufgabe 🙂 Zuerst müssen wir den Quellcode bei Thomas Fischl herunterladen. Dieser findet sich hier. Es wird die Datei usbasp.2011-05-28.tar.gz benötigt. Diese dann wie folgt entpacken:
tar -xzvf usbasp.2011-05-28.tar.gz
Heraus kommt ein Ordner usbasp.2011-05.28. Für die weiteren Schritte ist nur der Unterordner firmware relevant. Wie bereits im Abschnitt Hardware dargelegt, ist die Verbindung des GPIO PD3 mit dem USB D- PIN nicht vorgesehen. Die Initialisierung des DDRD Registers erfolgt in Zeile 310 in der Datei firmware/main.c
309 /* all outputs except PD2 = INT0 */ 310 DDRD = ~(1 << 2);
Ich habe diesen Code wie folgt angepasst:
309 /* all outputs except PD2 and PD3 = INT0 */ 310 DDRD = ~(1 << 2)&~(1 << 3);
Da im aktuellen avr-gcc Fehler gemeldet werden, wenn man Variablen, die nicht als “const” deklariert sind, in die read-only section legen möchte
usbdrv/usbdrv.h:455:6: error: variable 'usbDescriptorDevice' must be const in order to be put into read-only section by means of 'attribute((progmem))' char usbDescriptorDevice[];
müssen noch die folgenden Zeilen in den Dateien firmware/usbdrv/usbdrv.h und firmware/usbdrv/usbdrv.c um die Deklaration “const” erweitert werden.
diff für usbdrv.h:
$ diff usbdrv.h usbdrv.h-ORIG 455c455 < const char usbDescriptorDevice[]; char usbDescriptorDevice[]; 461c461 < const char usbDescriptorConfiguration[]; char usbDescriptorConfiguration[]; 467c467 < const char usbDescriptorHidReport[]; char usbDescriptorHidReport[]; 473c473 < const char usbDescriptorString0[]; char usbDescriptorString0[]; 479c479 < const int usbDescriptorStringVendor[]; int usbDescriptorStringVendor[]; 485c485 < const int usbDescriptorStringDevice[]; int usbDescriptorStringDevice[]; 491c491 < const int usbDescriptorStringSerialNumber[]; int usbDescriptorStringSerialNumber[];
diff für usbdrv.c
$ diff usbdrv.c usbdrv.c-ORIG 70c70 < PROGMEM const char usbDescriptorString0[] = { /* language descriptor */ PROGMEM char usbDescriptorString0[] = { /* language descriptor */ 80c80 < PROGMEM const int usbDescriptorStringVendor[] = { PROGMEM int usbDescriptorStringVendor[] = { 89c89 < PROGMEM const int usbDescriptorStringDevice[] = { PROGMEM int usbDescriptorStringDevice[] = { 98c98 < PROGMEM const int usbDescriptorStringSerialNumber[] = { PROGMEM int usbDescriptorStringSerialNumber[] = { 111c111 < PROGMEM const char usbDescriptorDevice[] = { /* USB device descriptor */ PROGMEM char usbDescriptorDevice[] = { /* USB device descriptor */ 142c142 < PROGMEM const char usbDescriptorConfiguration[] = { /* USB configuration descriptor */ PROGMEM char usbDescriptorConfiguration[] = { /* USB configuration descriptor */
Anschließend muss der Code kompiliert werden. Wer bisher nur Arduino kannte, weiß vielleicht nicht, was ein Makefile ist und wie man damit umgeht. Darauf gehe ich hier auch nicht weiter ein, sondern versuche nur eine nachvollziehbare Anleitung zu erstellen. Die notwendigen Open Source Command Line Tools wie avr-gcc (Compiler für Atmega Code) und avrdude (Tool zu Hochladen auf den Chip) kann man z.B. aus den Paketquellen von Ubuntu installieren. Die Tools werden aber auch von Arduino verwendet und daher mit jedem Arduino Paket ausgeliefert. Ich verwende im Folgenden die mit Arduino gelieferten Tools, da diese auf Windows, Linux und Mac OSX nach der Installation von Arduino vorhanden sein sollten. Leider ist das Tool “make” nicht im Arduino-Paket enthalten. Auf meinem Ubuntu 20.04 ist es aber in der Basis-Installation bereits installiert. Auf Windows und Mac OSX sind hier ggf. Nacharbeiten erforderlich.
Wie bereits im Bereich Hardware ausgeführt, ist der Basis-Chip ein Atmega88. Diese Tatsache müssen wir dem Compiler bekannt machen. Dazu wird die Datei firmware/Makefile editiert. Dort ist in Zeile 10, 11 und 12 Folgendes gesetzt:
10 TARGET=atmega8 11 HFUSE=0xc9 12 LFUSE=0xef
Entsprechend den vorstehenden Beispielen im Makefile ändern wir diese Werte auf die Werte aus dem Kommentar in Zeile 8 des Makefile. Der Atmega48 ist bezüglich der Fuses dem Atmega88 vergleichbar. Das sieht dann so aus:
10 TARGET=atmega88 11 HFUSE=0xdd 12 LFUSE=0xff
Damit der Build Prozess den mit Arduino angelieferten Compiler finden kann, muss die Pfad Variable ($PATH) entsprechend erweitert werden. Der Compiler liegt entweder direkt im Installationsverzeichnis von Arduino oder, wenn bereits ein Update via Boardmanager erfolgt ist, im entsprechenden User-Verzeichnis. Meine Basis-Installation von Arduino liegt unter /opt/arduino-latest, das User-Verzeichnis ist ~/.arduino15. Da Arduino 1.8.13 Stand 10.20202 sehr aktuell ist, setze ich also folgenden Pfad:
$ export PATH=$PATH:/opt/arduino-latest/hardware/tools/avr/bin
Danach werden die Tools gefunden und man kann sich die Version ausgeben lassen:
$ avr-gcc -v Using built-in specs. Reading specs from /opt/arduino-1.8.13/hardware/tools/avr/bin/../lib/gcc/avr/7.3.0/device-specs/specs-avr2 COLLECT_GCC=avr-gcc COLLECT_LTO_WRAPPER=/opt/arduino-1.8.13/hardware/tools/avr/bin/../libexec/gcc/avr/7.3.0/lto-wrapper Target: avr Configured with: ../gcc/configure --enable-fixed-point --enable-languages=c,c++ --prefix=/home/jenkins/workspace/avr-gcc-staging/label/debian7-x86_64/objdir --disable-nls --disable-libssp --disable-libada --disable-shared --with-avrlibc=yes --with-dwarf2 --disable-doc --target=avr Thread model: single gcc version 7.3.0 (GCC) $ avrdude -C /opt/arduino-latest/hardware/tools/avr/etc/avrdude.conf -v avrdude: Version 6.3-20190619 Copyright (c) 2000-2005 Brian Dean, http://www.bdmicro.com/ Copyright (c) 2007-2014 Joerg Wunsch System wide configuration file is "/opt/arduino-latest/hardware/tools/avr/etc/avrdude.conf" User configuration file is "/home/dt/.avrduderc" User configuration file does not exist or is not a regular file, skipping avrdude: no programmer has been specified on the command line or the config file Specify a programmer using the -c option and try again
Damit der avrdude seine Config-Datei findet, und um die richtige Baudrate für den Arduino ISP zu setzen, habe ich noch die Zeilen 79, 82 und 85 in firmware/Makefile angepasst. Diese ersetzen die vorhandenen Zeilen:
78 flash: 79 avrdude -C /opt/arduino-latest/hardware/tools/avr/etc/avrdude.conf -b 19200 -c ${ISP} -p ${TARGET} -P ${PORT} -U flash:w:main.hex 80 81 fuses: 82 avrdude -C /opt/arduino-latest/hardware/tools/avr/etc/avrdude.conf -b 19200 -c ${ISP} -p ${TARGET} -P ${PORT} -u -U hfuse:w:$(HFUSE):m -U lfuse:w:$(LFUSE):m 83 84 avrdude: 85 avrdude -C /opt/arduino-latest/hardware/tools/avr/etc/avrdude.conf -b 19200 -c ${ISP} -p ${TARGET} -P ${PORT} -v
Da zum Hochladen des Programms auf den Atmega88 ein Arduino ISP zum Einsatz kommt, muss diese Information noch entsprechend im Makefile hinterlegt werden. Vorsicht: Die Schnittstelle, bei mir /dev/ttyUSB0, kann abweichen und ist ggf. anzupassen. Dazu werden die Zeilen 20 und 21 in der Datei firmware/Makefile wie folgt angepasst:
20 ISP=stk500v1 21 PORT=/dev/ttyUSB0
Jetzt kann der Code kompiliert werden. Dazu sicherstellen, dass man sich im Verzeichnis firmware befindet und dort das Kommando “make main.hex” aufrufen:
$ make main.hex avr-gcc -Wall -O2 -Iusbdrv -I. -mmcu=atmega88 -c usbdrv/usbdrv.c -o usbdrv/usbdrv.o avr-gcc -Wall -O2 -Iusbdrv -I. -mmcu=atmega88 -x assembler-with-cpp -c usbdrv/usbdrvasm.S -o usbdrv/usbdrvasm.o avr-gcc -Wall -O2 -Iusbdrv -I. -mmcu=atmega88 -c usbdrv/oddebug.c -o usbdrv/oddebug.o avr-gcc -Wall -O2 -Iusbdrv -I. -mmcu=atmega88 -c isp.c -o isp.o avr-gcc -Wall -O2 -Iusbdrv -I. -mmcu=atmega88 -c clock.c -o clock.o avr-gcc -Wall -O2 -Iusbdrv -I. -mmcu=atmega88 -x assembler-with-cpp -c tpi.S -o tpi.o avr-gcc -Wall -O2 -Iusbdrv -I. -mmcu=atmega88 -c main.c -o main.o avr-gcc -Wall -O2 -Iusbdrv -I. -mmcu=atmega88 -o main.bin usbdrv/usbdrv.o usbdrv/usbdrvasm.o usbdrv/oddebug.o isp.o clock.o tpi.o main.o -Wl,-Map,main.map rm -f main.hex main.eep.hex avr-objcopy -j .text -j .data -O ihex main.bin main.hex
Sieht die Ausgabe wie im vorstehenden Block aus, dann hat alles geklappt und das Programm kann auf den Atmega88 hochgeladen werden.
Dazu prüfen wir, ob unser Arduino ISP den Atmega88 auf dem USB ISP ansprechen kann. Nach dem Verbinden des Nano Klon mit meinem Ubuntu zeigt der Befehl “dmesg” diesen als Device /dev/ttyUSB0 an:
[102115.734926] usb 1-1.3: new full-speed USB device number 13 using ehci-pci [102115.844481] usb 1-1.3: New USB device found, idVendor=1a86, idProduct=7523, bcdDevice= 2.54 [102115.844489] usb 1-1.3: New USB device strings: Mfr=0, Product=2, SerialNumber=0 [102115.844493] usb 1-1.3: Product: USB2.0-Serial [102115.845385] ch341 1-1.3:1.0: ch341-uart converter detected [102115.846405] usb 1-1.3: ch341-uart converter now attached to ttyUSB0
Nach der Dokumentation von Arduino ISP und avrdude, sollte ein auslesen der Chip-ID wie folgt möglich sein:
$ avrdude -C /opt/arduino-latest/hardware/tools/avr/etc/avrdude.conf -p m88 -c stk500v1 -P /dev/ttyUSB0 -b19200 -B5 avrdude: AVR device initialized and ready to accept instructions Reading | ################################################## | 100% 0.02s avrdude: Device signature = 0x1e930a (probably m88) avrdude: safemode: Fuses OK (E:F9, H:DD, L:FF) avrdude done. Thank you.
Das klappt auch wunderbar. Wie zu sehen ist, sind die H und L Fuses bereits auf 0xdd und 0xff gesetzt. Also analog den im Makefile eingetragenen Werten. Ein “make fuses” brauchen wir daher nicht. Als nächstes setze ich den Chip auf dem USB ISP vollständig zurück um ggf. gesetzte Lock-Bits zu löschen. Dazu kommt die “-e “ Option für einen chip erase zum Einsatz:
$ avrdude -C /opt/arduino-latest/hardware/tools/avr/etc/avrdude.conf -p m88 -c stk500v1 -P /dev/ttyUSB0 -b19200 -e avrdude: AVR device initialized and ready to accept instructions Reading | ################################################## | 100% 0.02s avrdude: Device signature = 0x1e930a (probably m88) avrdude: erasing chip avrdude: safemode: Fuses OK (E:F9, H:DD, L:FF) avrdude done. Thank you.
Im letzten Schritt wird jetzt die Firmware auf den Atmega88 geschrieben. Dazu sicherstellen, dass man im Verzeichnis firmware ist und dann das Kommando “make flash” aufrufen.
$ make flash avrdude -C /opt/arduino-latest/hardware/tools/avr/etc/avrdude.conf -b 19200 -c stk500v1 -p atmega88 -P /dev/ttyUSB0 -U flash:w:main.hex avrdude: AVR device initialized and ready to accept instructions Reading | ################################################## | 100% 0.02s avrdude: Device signature = 0x1e930a (probably m88) avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed To disable this feature, specify the -D option. avrdude: erasing chip avrdude: reading input file "main.hex" avrdude: input file main.hex auto detected as Intel Hex avrdude: writing flash (4532 bytes): Writing | ################################################## | 100% 6.47s avrdude: 4532 bytes of flash written avrdude: verifying flash memory against main.hex: avrdude: load data flash data from input file main.hex: avrdude: input file main.hex auto detected as Intel Hex avrdude: input file main.hex contains 4532 bytes avrdude: reading on-chip flash data: Reading | ################################################## | 100% 3.17s avrdude: verifying … avrdude: 4532 bytes of flash verified
Sieht die Ausgabe wie im vorstehenden Block aus, dann hat alles geklappt 😀 Der Nano kann vom USB Port und der USB ISP vom Nano getrennt werden. Auf dem USB ISP muss die Brücke an den “–>UP<–” Lötpads entfernt werden. Wenn man den USB ISP jetzt direkt in den USB Port des Rechners steckt, dann sollte dieser blau leuchten und in “dmesg” Folgendes zu sehen sein:
[108336.533661] usb 1-1.3: new low-speed USB device number 68 using ehci-pci [108336.650953] usb 1-1.3: New USB device found, idVendor=16c0, idProduct=05dc, bcdDevice= 1.04 [108336.650961] usb 1-1.3: New USB device strings: Mfr=1, Product=2, SerialNumber=0 [108336.650965] usb 1-1.3: Product: USBasp [108336.650968] usb 1-1.3: Manufacturer: www.fischl.de
Ein Hinweis noch: Ich hatte immer mal wieder Probleme mit dem Arduino ISP, weil es keinen Sync mehr mit dem USB ISP Atmega88 herstellen konnte. Ursache ist vermutlich der günstige CH34x USB2Serial Konverter auf den Nano Klonen. Ein Workaround ist, in avrdude die Baudrate auf 9600 (-b9600) zu stellen, einen erfolglosen Versuch durchzuführen, und danach wieder auf 19200 (-b19200) zu wechseln. Das hat bei mir immer dazu geführt, dass ein erneuter Upload möglich war. Nicht hübsch, aber gut genug.
Jetzt testen
Es steht noch der Beweis aus, dass es auch wirklich funktioniert. Mein Einsatzzweck war die Programmierung eines Attiny für unser Halloween Projekt. Dann nehmen wir doch gleich einen solchen Attiny und spielen da einen einfachen Blink Sketch auf. Details zum Arduino Core für den Attiny sind im Halloween Artikel zu finden.
Folgender Aufbau zeigt einen Attiny45 auf dem Steckbrett mit direkter Anbindung an den USB ISP sowie einer roten LED an GPIO PB3.
Der Zugriff auf das USB-Gerät muss als root erfolgen, solange keine entsprechende UDEV Regel für das Device angelegt ist. Als normaler User kann das Gerät nicht gefunden werden. Um dieses Problem zu beheben, kann man unter Ubuntu 20.04 als User root eine Datei /etc/udev/rules.d/99-USBasp.rules mit folgendem Inhalt anlegen:
# USBasp - USB programmer for Atmel AVR controllers # Copy this file to /etc/udev/rules.d so SUBSYSTEM!="usb_device", ACTION!="add", GOTO="usbasp_end" # USBasp www.fischl.de ATTR{idVendor}=="16c0", ATTRS{idProduct}=="05dc", MODE="660", GROUP="dialout" LABEL="usbasp_end"
Anschließend, ebenfalls als User root, die UDEV Regeln manuell neu laden oder das System rebooten und danach den USB ISP erneut einstecken.
udevadm control --reload-rules && udevadm trigger
Der Zugriff ist dann als normaler User möglich, wenn sich der User in der Gruppe “dialout” befindet. Dies ist auch das Standardvorgehen von Arduino. Jetzt lese ich zum Test die ID des Attiny per avrdude und mit dem umgebauten USP ISP an der Command Line aus:
$ avrdude -C /opt/arduino-latest/hardware/tools/avr/etc/avrdude.conf -p attiny45 -c usbasp -B5 avrdude: set SCK frequency to 187500 Hz avrdude: AVR device initialized and ready to accept instructions Reading | ################################################## | 100% 0.00s avrdude: Device signature = 0x1e9206 (probably t45) avrdude: safemode: Fuses OK (E:FF, H:DD, L:E1) avrdude done. Thank you.
Zur Verwendung in der Arduino Oberfläche wird zuerst das Board ausgewählt. Hier ist es ja eigentlich nur der Baustein selber. Dies erfolgt wieder im Menu “Werkzeuge”, wie im folgenden Screenshot dargestellt. Bitte auch ganz unten im Menu den Punkt “Programmer:” beachten. Dort ist “USPasp (ATTinyCore)” ausgewählt.
Dann einen einfachen Blink Sketch erstellen und diesen durch einen Klick auf den “Pfeil” links oben auf den Attiny45 übertragen.
In der Ausgabe im “schwarzen Kasten” unten in der Arduino Oberfläche sieht man an den “set SCK frequency” Zeilen, dass der Upload durch den USP ISP Umbau erfolgt ist. Und blinken tut es auch:
Wenn euch dieser Artikel gefällt und nützlich ist, dann könnt ihr mir eine Freude machen, indem ihr Thomas Fischl und/oder Arduino auf deren Seiten ein paar Euro, z.B. via Paypal, spendet. Freie Software bedeutet nicht zwingend kostenlose Software. Sondern in erster Linie frei in der Verwendung und Anpassung. Nur daher ist dieser Artikel überhaupt möglich. Und wenn wir wollen, dass es in Zukunft weiterhin Entwickler gibt, die uns solche Software einfach zur Verfügung stellen, dann sollte man auch hier und da etwas zurück geben. Vielen Dank!