Vergleich der Implementierung (Teil 5)

Die Finanzverwaltung existiert nun in zwei Architekturen. Nun kein ein Vergleich erfolgen. Welches ist für eine private Anwendung die sinnvollere Architektur?

Die folgenden Kriterien zieht der Vergleich heran:

  • Größe der Programmdatei
  • Performance
  • Komplexität der Weiterentwicklung

Nach dem Vergleich folgt die Bewertung. Hierdurch ist die Entscheidung für eine zu wählende Architektur möglich.

Größenvergleich

Ohne dem kommen die Kerl doch nie aus *g*. Allerdings ladet der Inhalt der Archive erst mal im Speicher. Folglich startet das Programm mit dem kleineren Archiv schneller und läßt mehr Freiraum für Daten. Mit 26,8 zu 44,7 MB haben hier klar die Events die Nase vorn. Bei den Events entfällt JPA mit allen zugehörigen Bibliotheken zudem die Datenbank. Folglich steckt hier auch sehr viel weniger in der Jar-Datei.

Vergleich der Performance

Den Vergleich der Performance gilt es zu differenzieren. Ich mag einmal die Event-Variante mit einer echten Datenbank-Version vergleichen, beide male landen die Daten in einer Datei. Allerdings teste ich auch die In-Memory-Datenbank, hierdurch versuche ich einen Vergleich zwischen der reinen Architektur zu ermöglichen. Denn hier zählt nicht die Zeit eines Dateizugriffs.

Die einzelnen Werte fasst die folgende Tabelle zusammen:

Kriterium Redux JPA-In-Memory JPA-Datei
Start-Zeit ohne Daten 2 s 4 -5 s 5 s
Start-Zeit mit Daten 2,5s  – 4,4 s
Konto lesen ohne Daten 4-5 ms 5 ms 5 ms
Konto lesen mit Daten 3 ms 4-5 ms 5 ms
Buchungen lesen 3-4 ms 11-12 ms 11 ms
Buchen 4 ms 12 ms 12 ms

Zunächst gilt es zu bemerken: wer misst misst Mist! Die Zahlen sind natürlich abhängig von meinem Rechner und was er sonst noch zu tun hat.

Zunächst gab es jeweils ein Warm-Up mit 3 lesenden bzw. 5 schreibenden Aufrufen. Die Buchungen je Konto habe ich gelesen nach 5 Warm-Up + 50 Buchungen. Mehr Buchungen auf einen Streich kann keine Oberfläche sinnvoll darstellen und folglich unrealistisch. Anschließend folgten weiter 1000 Buchungen, damit für einen Start mit Buchungen auch Futter da war. Die Startzeit mit Buchungen ist für Redux eine Herausforderung. Bevor es weiter arbeiten kann, hat es alle alten Actions zu lesen und zu verarbeiten. Natürlich dauert das umso länger, je mehr Action vorliegen.

Allgemein ist die Event-Verarbeitung schneller als die Datenbank-Alternative. Schließlich gibt es auch weniger technischen Overhead. Trotz der gut 1000 Buchungen ist selbst die Startzeit geringer als mit der Datenbank. Ich habe mal einen Start mit 10.000 Buchungen gemacht. Damit Dauerte der Redux-Start etwa so lange, wie die JPA.
Der Vergleich zwischen einer in Memory und einer in die Datei schreibenden Datei bringt dagegen keine nennenswerten Unterschiede.

Vergleich der Erweiterbarkeit

Wie steht es um die Erweiterbarkeit? Dazu möchte ich die Arbeitsschritte für das Einfügen einer neuen Entität aufzählen.

Schritte für die JPA-Implementierung

  • Zunächst ist eine Modell-Klasse anzulegen.
  • Die Modell-Klasse benötigt JPA-Annotationen
  • Es folgen das Repository und ggf.
  • Serviceklassen zur Darstellung neue Anwendungsfälle
  • Das Datenbankschema ist zu erweitern

Schritte für Redux

  • Auch hier ist eine Modell-Klasse anzulegen
  • Es folgen Action und Reducer für die neuen Anwendungsfälle
  • ggf. ist der Status zu erweitern.

Vergleich zwischen den Implementierungen

Ein generisches Speichern von Actions vorausgesetzt, ist Redux praktisch leichter umzusetzen. Die verwendeten Technologie ist dort schlicht Java. Reducer ggf. Filter auf den State sind reine Funktionen und somit auch recht leicht testbar. Im Gegensatz dazu sind im JPA-Umfeld unterschiedliche Techniken zu beherrschen: Annotationen, Spring-Data-Konventionen bzw. JQL-Abfragen, SQL für’s Schema – das ganze Datenbankabhängig. Abschließend sind etliche Tests eher Integrations- als Unittest.

finally

Unter dem Strich ist Redux kleiner und schneller, jedenfalls bei dieser Datenmenge. Bei einem deutlich größeren Datenvolumen, käme der Speicher des Redux ohne weitere Maßnahmen an seine Grenzen. Die notwendigen Erweiterungen könnten dann für die Datenbank sprechen.

Die gemessenen Zeiten sind generell für ein kleines Programm im Heimgebrauch zu vernachlässigen. Umso mehr wiegt die Erweiterbar- und Wartbarkeit. Hier ist der Redux-Ansatz weniger komplex.

Schnell und einfach spricht bei den gegebenen Annahmen mehr für den Redux-Ansatz. Folglich werde ich ihn weiter vorantreiben.

Happy coding!

Events zur Umsetzung einer Finanzverwaltung (Teil 4)

Im letzten Teil habe ich die kleine Finanzverwaltung mit Hilfe einer relationalen Datenbank und der Java Persistence API realisiert. Diesmal möchte ich eine Alternative zu diesem gängigen Setup umsetzen. Ich versuche eine Umsetzung mit Events, die das Programm einfach in einer Datei speichert.

Warum eine Alternative mit Events?

Die Programmierung gegen eine Datenbank hat aufgrund der “objektrelationalen Unverträglichkeit” ihre Tücken. Erweiterungen z.B. Vererbungen machen Abfragen komplex und machmal langsamer.

Für die heimische Applikation ist eine Datenbank möglicherweise übertrieben. Zumal die die Datenmenge ist überschaubar ist. Sind es in einem Monat 25 Buchungen, so scheint mir das viel zu sein. Auf Sicht von 10 Jahren kommen so gerade mal 3000 Buchungen zusammen. Die passen heutzutage entspannt in den Speicher. Folglich reicht es die Buchungen in einer Datei zu speichern und beim Programmstart wieder zu lesen.

Dieses Vorgehen ist auch Event Sourcing, wofür Buchhaltung ein Paradebeispiel ist. Die Buchungen bilden die Kontosalden, praktisch der Status des Systems zu einem Zeitpunkt. Die Finanzverwaltung kann ihn durch erneutes “abspielen” der Buchungen jederzeit wieder herstellen.

Ein Status kann als Snapshot gespeichert sein. Dieser historischer Zustand – in diesem Beispiel die Kontostände – ist dann schnell gelesen, ohne alle Buchungen erneut lesen zu müssen. Architektonisch geht dieser Ansatz in Richtung CQRS.

Ein “Events” basiertes System: Redux

In der Webprogrammierung gibt es mit Redux ein Event-basiertes Vorgehen, um den Status im Client zentral zu verwalten. Mir gefällt dieser Ansatz sehr gut, da er das Event-Prinzip sehr schlicht umsetzt.

Eine Action ist der Anstoß zu einer Änderung des Status. Den ermittelt der Reducer, bei dem es sich um eine Funktion handelt. Die Argumente dieser Funktion sind die Action und der aktuelle State. Das Ergebnis ist ein neuer State. Dieser berücksichtigt die Änderungen, die eine Action auslöst.

Redux-Prinzip

Die Action ist ein unveränderliches Objekt, welches für die Umsetzung erforderliche Daten enthalten kann.
Wie bereits erwähnt ist der Reducer eine Funktion. Ein definierter Status führt mit der gleichen Action zu einem immer gleichen neuen State, wodurch der Reducer leicht testbar ist.
Speichere ich also die Actions und ermittle – ausgehend von einem initialen Status – sukzessive den aktuellen Status, betreibe ich mit wenigen Elementen letztlich nichts anderes als Event Sourcing.

Die Schnittstelle für die Programmierung ist der Store.

Der Store speichert den State und stellt die Dispatch-Operation bereit. Sie ermittelt mit Hilde des Reducers und der übergebenen Action den neuen State und ersetzt den alten.
Der Stores ermöglicht dem Programmierer auch Zugriff auf den Status. Ebenfalls bietet der Store das verfolgen von Änderungen an (Observer-Pattern). In Javascript-artigen Sprachen steht dafür die Möglichkeit Fallback-Funktionen zu registrieren (“subscribe()”). Die Java-Konvention ist dagegen eher das registrieren eines Listeners.

In Webclient-Implementierungen lösen die Abarbeitung von Actions sogenannte Effekte aus. Die setzen dort Seiteneffekte um, insbesondere der Zugriff auf ein Backend. In der Finanzverwaltung spielt das erst mal keine Rolle, denn das Redux-Prinzip soll ja gerade im Backend umgesetzt werden.

Eine einfache Redux-Implementierung

Als Programmierschnittstelle dient ein Store, dessen Schnittstelle ich – wie zuvor beschrieben – erst mal allgemein definiere:

public abstract class Store<T extends State> {

	protected T state;
	protected List<Listener> listeners = new ArrayList<>();
	
	public Store(StateInitializer<T> initializer) {
		state = initializer.initialState(this);
	}
	
	public abstract void dispatch(Action<?> action);

	public T getState() {
		return state;
	}
	
	public void addListener(Listener listener) {
		listeners.add(listener);
	}
}

Der erste Teil dieser Reihe hat drei Anwendungsfälle beschrieben. Buchungen zu erstellen ist die einzige schreibende Funktion. Sie ist als Action zu implementieren, da sie den Status der Anwendung ändert.

Die Umsetzung erfolgt in der Dispatch-Methode. Sie könnte z.B. generisch registrierten Reducer-Funktionen weiterleiten. Die damit verbundene Komplexität ist für eine erste Implementierung jedoch unnötig.

	@Override
	public void dispatch(Action<?> action) {
		switch (action.getType()) {
		case BuchenAction.TYPE:
			state = BuchhaltungReducer.buche(state, (BuchenAction)action);
			break;
		default:
			throw new IllegalArgumentException("ungekannte Action: "+action.getType());
		}
		listeners.forEach( listener -> listener.fireAction(action) );
	}

Der BuchhaltungState unterscheidet zwischen Grundbuch und Hauptbuch, wodurch die typische Sicht auf die Buchungen und Konten gegeben sind. Diese sind Listen und zeigen auf Objekte, deren Relationen wiederum auf Objekte zeigen. Durch diese Pointer liegen die Konten und Buchungen nur einmal miteinander verknüpft im Speicher vor. Deshalb entfallen Redundanzen und die Speicherbelastung ist vergleichsweise gering.
Der State ist als unveränderliches Objekt vorgesehen. Im Ideal sollte ein neuer State eine Kopie mit Änderungen sein. Zumindest ist dieses Vorgehen sinnvoll, wenn der Store parallel Angesteuert würde.

Im Code der Dispatch-Methode ist die statische Reducer-Funktion zu erkennen. Sie übernimmt eine Buchung aus der Action, baut alle Relationen zu den beteiligten Objekten auf und sorgt für die Anpassung der Salden. Die Programmierung für den Prototyp ist dabei recht “schmutzig”:

	public static BuchhaltungState buche(BuchhaltungState state, Action<?> action) {
		BuchenAction buchenAction = (BuchenAction)action;
		Buchung buchung = buchenAction.getPayload();

		state.getGrundbuch().add(buchung);
		
		buchung.getUmsaetze().stream().forEach( umsatz -> {
			umsatz.setBuchung(buchung);
			
			Konto konto;
			try {
				konto = selectKontoByName(state, umsatz.getKonto().getName());
			} catch (UnkownEntityException e) {
				konto = new Konto();
				konto.setName(umsatz.getKonto().getName());
				state.getHauptbuch().add(konto);
			}
			konto.verbuche(umsatz);
		});
		
		return state;
	}

Einmal erzeugt sie den State nicht neu. Das wäre technisch aufwendig und würde den Code erst mal nur aufblähen.
Nicht vorhandene Konten legt die Methode direkt an. Eine konsequente Umsetzung müsste mit eine “Konto-Anlegen-Action” arbeiten, wobei diese wiederum fachlich zu hinterfragen ist. Diese einfache Implementierung ist erst mal ausreichend, um mit der relationalen Implementierung verglichen zu werden.
BuchenAction sind wieder unveränderliches Objekte, die eine Buchung aufnehmen. Die Action verwendet Modell-Klassen. Die Buchung bildet die Spitze einer Objekt-Hierarchie. Sie ist leicht zu serialisieren, indem über diesen Baum iteriert wird.

Aus dem Status sind nun die Konten und deren Buchungen zu lesen, um lesenden Anwendungsfälle zu realisieren. Filter sind wieder statische Funktionen, die den Status und ggf. Parameter erhalten.

	public static Konto selectKontoByName(BuchhaltungState state, String kontoName) {
		return state.getHauptbuch().stream()
				.filter( konto -> konto.getName().equals(kontoName))
				.findFirst()
				.orElseThrow( () -> new UnkownEntityException("Konto mit Namen: "+kontoName));
	}
	
	public static List<Konto> selectKonten(BuchhaltungState state) {
		return state.getHauptbuch();
	}
	
	public static List<Buchung> selectBuchungByKontoName(BuchhaltungState state, String kontoName) {
		return selectKontoByName(state, kontoName)
			.getUmsaetze().stream()
			.map( umsatz -> umsatz.getBuchung())
			.collect(Collectors.toList());
	}

Redux in die API einbauen

Der Controller benötigt einen Store. Mit Filter und Action bildet er dann die API ab.

	@GetMapping("konto")
	public List<Konto> findAllKonten() {
		return kontoMapper.domainKontoListToApiKontoList(
				selectKonten(store.getState()));
	}
	
	@GetMapping("konto/{kontoName}/umsatz")
	public List<Buchung> findKontoBuchungenByKontoName(@PathVariable("kontoName") String kontoName) {
		return buchungMapper.domainBuchungListToApiBuchungListe(
				selectBuchungByKontoName(store.getState(), kontoName));
	}

	@PostMapping("buche")
	public ResponseEntity<String> buche(@RequestBody Buchung buchung) {
		
		try {
			store.dispatch(
					BuchhaltungActions.buche(
							buchungMapper.apiBuchungToDomainBuchung(buchung)));
		} catch (Exception e) {
			return ResponseEntity.unprocessableEntity().body("Fehler: " + e.getMessage());
		}
		
		return ResponseEntity.accepted().body("Verarbeitet");
	}

Speichern und Laden der Daten

Der Store ist die zentrale Redux-API. Er realisiert auch das speichern und lesen von Daten. Für den BuchhaltungStore ist ein LogListener pflicht. Dieser hängt eine ausgeführte Action an eine Action-Datei an.

Sobald der Store intern initialisiert ist, liest er diese Datei ein und führt die gelesenen Actions aus, wodurch er den bei Programmende erreichten Status wieder hergestellt.

finally

Nun existieren zwei Prototypen für eine Finanzverwaltung, die es zu vergleichen und bewerten gilt. Davon beim nächsten mal mehr.

Happy coding!

 

 

 

Implementieren der Finanzverwaltung mit JPA (Teil 3)

Dieser Beitrag beschreibt die Implementierung der Schnittstelle aus dem letzten Teil. Zur Speicherung dient eine relationale Datenbank – H2. Auf sie greift das Programm mittels der Java Persistente API (JPA) in der “Geschmacksrichtung” Spring-Data-JPA zu.

Implementieren der Datenbankanbindung

Das Modell mit JPA-Annotationen implementieren

Für die Speicherung der Daten in eine relationale Datenbank kommt die Java Persistente API (JPA) zum Einsatz. Die Abbildung der Model-Klassen in ein Datenbank-Schema erfolgt dabei mittels Annotationen.

@Data
@Entity
public class Buchung {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	private String verwendung;
	private String empfaenger;
	
	@ToString.Exclude
	@OneToMany(mappedBy = "buchung", cascade = CascadeType.ALL, 
                      fetch = FetchType.EAGER, orphanRemoval = true)
	private List<Umsatz> umsaetze = new ArrayList<>();
	
	public void addUmsatz(Umsatz umsatz) {
		getUmsaetze().add(umsatz);
	}
}

@Data
@Entity
public class Umsatz {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	private LocalDate valuta;
	private BigDecimal betrag;
	
	@ManyToOne
	@JoinColumn(name = "konto_id", nullable = false,
                    updatable = false)
	private Konto konto;
	
	@ManyToOne
	@JoinColumn(name = "buchung_id", nullable = false, 
                    updatable = false)
	private Buchung buchung;
}

@Data
@Entity
public class Konto {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;
	private String name;
	private BigDecimal saldo = BigDecimal.ZERO;
	
	@OneToMany(mappedBy = "konto")
	private List<Umsatz> umsaetze = new ArrayList<>();

	public void verbuche(Umsatz umsatz) {
		umsaetze.add(umsatz);
		saldo = saldo.add(umsatz.getBetrag());
	}
}

Technisches Setup

Das Umfeld und die Zugriffe ist durch Spring-Data-JPA einfach zu implementieren. Die Umsetzung ist analog des JPA-Guides implementiert.

Als Datenbank dient H2, die als Abhängigkeit im Projekt eingebunden ist. Sie steht damit im Klassenpfad. Der Spring-Data-Starter sorgt ohne zusätzliche Konfiguration erst mal für eine In-Memory-Datenbank.

In der Datei application.properties ist die Eigenschaft spring.jpa.hibernate.ddl-auto=update gesetzt. Spring-Data verwendete Hiberante, das bei jedem Start das Datenbankschema prüft. Da die In-Memory-Datenbank nach dem Start leer ist, entsteht hier immer ein vollständige Schema.

Die Datenbankzugriffe implementieren

Die eigentlichen Zugriffe implementieren Repository-Interfaces, die CrudRepository von Spring-Data erweitern. Typische Zugriffe, wie Speichen, alle Daten laden, Löschen, etc. sind damit durch den Framework gegeben.

Spring Data ermöglicht das Erstellen von Abfragen auf Attribute, indem einer Abfrage-Methode eine Konventionen der Benennung einhält. Konten sollen per Name zu finden sein. Diese einfache Abfrage ist gegeben durch:

Konto findByName(String name);

Einzig das Laden der Buchungen je Konto ist etwas komplexer, deshalb ist ein Ansatz via Konvention der Benennung hier nicht sinnvoll. Hier hilft eine @Query-Annotation mit einer Abfrage, die in JPAs Quere-Language definiert ist.

public interface BuchungRepository extends CrudRepository<Buchung, String>{
	@Query(
	 "select b from Buchung b "
	 + "join b.umsaetze u "
	+ "where u.konto.name = :kontoName "
	+ "order by u.valuta desc, b.id desc ")
	List<Buchung> findByKontoName(String kontoName);
}

Implemtentieren der Service-Klassen

Lesende Funktionen

Die Controller-Klasse des letzten Teil delegiert alle Logik an Service-Klassen. Die lesenden Anwendungsfälle setzten die Repository-Interfaces um, an die Service-Klassen Abfragen einfach nur weiterleiten. Die Service-Ebene als Zwischenstation hat allerdings Vorteile.

  • Service-Klassen sind zentraler Ansatzpunkt, an dem Transaktionsverhalten definiert werden kann.
  • Die API-Ebene kann auf zusätzliche Importe von Repositories verichten.

Schreibende Funktion: buchen

Die Kern-Funktion ist somit im BuchungService das buchen, das letztlich Jahrhunderte alte Praxis der doppelten Buchführung technisch umsetzt:

  1. Der Buchführende trägt Geschäftsvorfälle laufend in ein Journal (Grundbuch) ein. (Anwendungsfall “Buchung erstellen”)
  2. Anschließend erfolgt ein Übertrag der Kontoumsätze je Konto in das Hauptbuch, wodurch die Bewegungen auf einem Konto sichtbar sind (ermöglicht den Anwendungsfall “Umsatz anzeigen”)
  3. Abschließend erfolgt die Saldierung der Konten und der aktuelle Kontostand ist auf den Konten nachvollziehbar (Anwendungsfall “Konten abfragen”)

Die Reihenfolge der Bearbeitung ist dabei insbesondere der Abhängigkeiten der Datenbank geschuldet.

@Transactional	
public void buche(Buchung buchung) {
  for (Umsatz umsatz : buchung.getUmsaetze()) {
    			
    Konto konto = kontoRepository.findByName(umsatz.getKonto().getName());
    if (isNull(konto)) {
      konto = kontoRepository.save(umsatz.getKonto());
      konto.setSaldo(BigDecimal.ZERO);
    }
    umsatz.setKonto(konto);
    umsatz.setBuchung(buchung);
    konto.getUmsaetze().add(umsatz);

    konto.setSaldo(konto.getSaldo().add(umsatz.getBetrag()));

    kontoRepository.save(konto);
  }
  buchungRepository.save(buchung);	
}

Zunächst erfolgt das Buchen in einer Transaktion. Ich spreche alle Konten an oder mache keine Änderung. Es kommt so nicht zu einer plötzlichen Geldvermehrung, ohne dass auch jemand zahlt.

Die Verarbeitung arbeitet zunächst alle in der übergebenen Buchung genannten Umsätze ab.
Zu jedem Umsatz der übergebenen Buchung sucht die Methode zunächst ein Konto mit dem genannten Namen. Sollte es nicht existieren, so wird dieses Konto angelegt.
Als nächstes entstehen die bidirektionalen Relationen zwischen den Entitäten Buchung-Umsatz-Konto. Sie sind in der übergebenen Struktur einseitig schon vorhanden, benötigen aber noch die technische Gegenrichtung.

Abschließend berechnet und setzt die buchen-Methode den neuen Saldo des Kontos und speichert es.

Nach der Verarbeitung ist nun noch die Buchung selbst zu speichern. Dabei wandern auch die zugehörigen Umsätze in der Datenbank, da diese im JPA-Mapping kaskadierend an die Buchung gebunden sind.

finally

Damit liegt eine erste, einfache Implementierung vor. Sie ist mehr aus einem Reflex entstanden, der im beruflichen Kontext geübt ist. Gibt es nicht noch einen weiteren Weg, um eine Finanzverwaltung für den Heimgebrauch zu implementieren? Doch die gibt es. Dazu im nächsten Teil mehr. Anschließend möchte ich beide Implementierungen vergleichen, und den vielversprechenderen Weg weiter implementieren.

Happy coding!

Winterprojekt: Setup Arduino-Programmierung

Der Arduino Uno ist eingetroffen. Nun kann es mit dem Winterprojekt weitergehen. Zuvor ist aber etwas Installationsaufwand zu treiben.

Für den Uno gibt es eine Entwicklungsumgebung, die weiterhin auch für andere Controller verwendbar ist. Auf der Homepage finden sich neben vielen Informationen und Anleitungen die Download-Seite mit dem Installer der IDE. Das ist die Abkürzung für integrierte Entwicklungsumgebung. Sie kann hier jeder passend zum jeweiligen Betriebssystem herunterladen.

Der Arduino unterhält sich technisch über eine serielle Schnittstelle, wie sie bei den ersten Mäusen benutzt wurde. Die gibt es heute aber an keinem Rechner mehr. Aber es gibt USB, den universellen seriellen Bus. Dafür bringt der Installer gleich einen passenden Treiber für die Kommunikation des Arduino mit dem Computer mit. Die USB-Geräte unterhalten sich auch seriell und deshalb sorgt der Treiber für eine Übersetzung zwischen den beiden seriellen Varianten.

Hinweis:
Bei einer Internetsuche können zwei Seiten gefunden werden: arduino.cc und arduino.org. Auch in die Welt der offenen Soft- und neuerdings auch Hardware sind die Menschen sich ebenfalls nicht immer einig. Das ist hier genauso. Ich bevorzuge die erste Adresse. Sie ist die ursprüngliche und ich vermute deshalb wirken mehr Menschen mit. Das lässt mehr Stabilität erwarten.

Aufbau

Steckt man ein neues USB-Gerät an einen Computer, dauert es immer einen Moment bis es erkannt wird. Die IDE muss den Arduino ebenfalls erkennen, folglich stecke ich ihn an den Rechner bevor ich die Umgebung starte.

Die Installation ist abgeschlossen. Der Uno ist mit dem Computer verbunden und anschliessend die IDE gestartet. Zeit das Zusammenspiel der beiden zu testen. Bei Programmiersprachen ist es üblich, zunächst ein “Hallo World” auszugeben. Klingt albern, zeigt aber deutlich: die Umgebung ist eingerichtet und funktioniert. Der Arduino hat keinen Bildschirm für eine Ausgabe, aber eine kleine Leuchtdiode auf der Platine. Das mitgelieferte Beispielsprogramm Blink läßt diese Diode blinken.

Arduino-IDE

Und da ist auch schon die erste Hürde. Der Arduino ist mit dem Rechner verbunden, bloß das die  Diode auf dem Board bereits zu blinken beginnt. Das heisst, der Hersteller verwendet dieses Programm offensichtlich für den Test des Aufbaus. Das stört aber nur bedingt. Das Blinken ist gleichmäßig und das Programm kann einen abweichenden Rhythmus vorgeben, so dass ein Unterschied zu erkennen ist.
Nun also erst mal die IDE starten. Im erscheinen Fenster ist ein Programmrumpf zu sehen. Darüber befinden sich Icons. Das Häkchen-Icon ganz Links überprüft die KorreStartfenster der IDEktheit eines Programmes. Es folgt das Icon, dass ein Programm auf das Board überträgt. Die folgenden dienen dem Öffnen, Speichern und beginnen neuer Programmdateien. Die IDE verrät die Funktion in einem Hilfetext, sobald der Mauszeiger über den Icon verweilt. Unter dem Programmcode-Fenster befindet sich die Konsole. Sie enthält die Ausgaben der Umgebung. Fortschritt beim Verarbeiten und Übertragen oder entdeckte Fehler im Code.

Beim öffnen eines neuen Programms – die Arduino IDE nennt dies Sketch – sind die Rümpe einer setup() und der loop()-Funktion zu erkennen. Für setup() gilt: der Name ist Programm. Hier können Variablen oder Hardware initialisieren werden. Die loop()-Funktion wird in einer Endlosschleife ausgeführt und enthält das, was der Controller verrichten soll.

Konfiguration

Bevor das Blink-Programm gestartet wird, ist die Entwicklungsumgebung noch zu konfigurierten. Im Menü “Werkzeuge” stelle ich unter “Platine” den “Arduino/Genunio Uno” ein. Unter Port ist die serielle Schnittstelle auszuwählen, die der Treiber zur für die Kommunikation mit dem Board bereitstellt. Bei meinem Mac heisst sie “/dev/usbmodem411” auf Windows erscheint ein “COM” gefolgt von einer Nummer. Im Idealfall weisst die IDE darauf hin, dass mit Port ein Board verbunden ist.

Blinkprogramm aktivieren

Das Test-Programm, “Blink”, ist eins von mehren mitgelieferten Beispielen. Es ist wie im Bild gezeigtPfad zum Blinkprogramm im Datei-Menü zu finden. Die Beispiele liefern Vorlagen für die verschiedenen Themen, in denen sich angehende Bastler zu unterschiedlichen Themen orientieren können.

Alldieweil ist das Testprogramm Blink geöffnet. An Pin 13 ist die Leitdiode (LED) auf dem Board angeschlossen. Deshalb definiert die setup()-Funktion diesen Pin als Ausgang. In loop() wird nun

  1. die LED eingeschaltet,
  2. eine Sekunde gewartet,
  3. die LED ausgeschaltet,
  4. wieder eine Sekunde gewartet
  5. und das ganze beginnt von vorn.

Damit eine die Wirkung des Programmes zu sehen ist, verändere ich das An-/Aus-Verhältnis der LED zu

void loop() {
  digitalWrite(13, HIGH);
  delay(200); // warte nur kurz
  digitalWrite(13, LOW);
  delay(800); // pausiere deutlich länger
}

Noch einem Klick auf das Hochladen-Icon, anschliessend sind in der Konsole die IDE-Aktivitäten zu verfolgen: Übersetzen und Packen des Programmes und die folgende Übertragung. Sobald sie startet, ist ein “Flimmern” zweier Kontrolle-LEDs auf dem Board zu erkennen. Anschließend blinkt die Diode auf dem Board nicht mehr gleichmäßig, sondern blitzt entsprechend kurz auf.

Ein Programm wurde übertragen, anschliessend wird es ausgeführt und die LED blitzt.