Einführung Arduino mit PlatformIO

Dieser Artikel ist die Aufzeichnung eines Arduino-Einführungsworkshop inkl. aller Beispielprogramme und einiger kurzer Erklärungen. 

Im Maker Space haben wir verschiedene Bauformen von Microcontrollern vorrätig. Jeder hat seine eigenen Besonderheiten und Einsatzgebiete.

  • Arduino Uno – Einsteigermodel, Relativ viele Pins
  • Arduino Mega – Sehr viele Pins, sehr große Bauform
  • Lillipad – Wasserfest, für den Einsatz in Textilien
  • Arduino Nano – klein und günstig
  • ESP32 – WLAN und Bluetooth

Für den Workshop wird der Arduino Nano eingesetzt. Grund dafür ist, dass dieser Microcontroller verhältnismäßig günstig ist (China ca. 1€, Deutschland ca. 10€).

Als Entwicklungsumgebung wird im Workshop Visual Studio Code (Download) mit dem Plugin PlatformIO (Dokumentation) verwendet. Gegenüber der Arduino IDE (Download) bieten sich verschiedene Vorteile. Zum einen ist die Unterstützung inklusive Treiber auch für günstige Boards direkt mitgeliefert. Autokorrektur und übliche IDE-Features wie Code-Vervollständigung, Syntax-Highlighting, markieren von Syntaxfehlern werden direkt von VSCode übernommen. Einfaches uploaden und automatische Portauswahl bringt PlatformIO mit.

Installation und Einrichtung

Die Installation von Visual Studio Code ist über den Installer von der Website und wird hier nicht groß erklärt.

PlatformIO ist ein Plugin für VSCode und wird innerhalb des Editors installiert. Dazu muss zuerst Visual Studio Code gestartet werden.

Visual Studio Code Startbildschirm

Danach muss zuerst das Plugin installiert werden. Dazu öffnen wir links im Menü den Bereich Extensions.

Reiter für Plugins öffnen

Danach müssen wir das Plugin suchen und installieren. Dazu oben in die Suche den Begriff “PlatformIO” eingeben und beim Plugin “PlatformIO IDE” auf install klicken.

Plugin suchen und installieren

Jetzt müssen wir warten, bis das Plugin fertig installiert ist. Anschließend Visual Studio Code neu starten. Nach dem Neustart werden noch einige Dinge installiert. Sobald alles abgeschlossen ist und der PlatformIO Startbildschirm angezeigt wird können wir loslegen.

Wenn die Installation erfolgreich war, wird in der unteren linken Ecke ein kleines Haus-Symbol angezeigt. Dieses Symbol öffnet auch den PlatformIO-Startbildschirm.

CH340 Treiber
CH340 Treiber

Projekt anlegen

In dem PlatformIO Startbildschirm haben wir unterschiedliche Werkzeuge zur Auswahl. Wir können zuerst ein neues Projekt anlegen. Dazu klicken wir auf “New Project”. Im folgenden müssen wir einen Projektname festlegen, unser Board und das Framework auswählen und den Speicherort bestimmen. Für den Workshop ist das Board der “Arduino Nano ATmega328” und als Framework setzen wir Arduino ein. Das Projekt wird standardmäßig im Benutzerverzeichnis unter Dokuments/PlatformIO/Projects gespeichert.

Neues Projekt anlegen
Neues Projekt anlegen

Sobald das erledigt ist und der Projektwizard das Projekt fertig initialisiert hat können wir uns im Explorer das Projekt ansehen. Der Explorer ist im linken Menü zu finden und zeigt alle Dateien des Projekts in einer Baumstruktur an. Für den Einstieg interessiert uns der Ordner “src” in dem wir die Datei “main.cpp” finden. Diese Datei können wir durch Doppelklick öffnen.

Explorer und main.js
Explorer und main.js

Beispielprogramme

In die main.cpp kommt unser Programmcode. Im Folgenden sind die drei Beispielprogramme aus dem Workshop. Jedes Arduino-Programm beginnt mit zwei Funktionen.

Die Setup-Funktion wird beim starten des Arduino ausgeführt. In dieser Funktion werden alle Definitionen und Initialisierungen ausgeführt. Viele Bibliotheken bringen Setupfunktionen mit, die hier aufgerufen werden müssen z.B. FastLed mit FastLED.addLeds<NEOPIXEL, 6>(leds, 1);

Die Loop-Funktion wird endlos lange immer wieder nacheinander aufgerufen. Immer wenn die Loop beendet wurde beginnt ein neuer Aufruf. Alle Logik, die wir von unserem Microcontroller ausgeführt haben wollen werden wir in diese Funktion implementieren.

Blinkende LED

Auf dem Arduino Nano ist eine LED direkt auf dem Board verbaut. Diese kann für einfache Funktionstests und kleine Beispiele verwendet werden. Die Konstante LED_BUILTIN enthält den Pin dieser LED. Das Programm setzt im Setup diesen Pin als output um dann in der Loop den Pin abwechselnd auf High und Low, also an und aus, zu setzen.

				
					#include <Arduino.h>

void setup() {
  pinMode(LED_BUILTIN, OUTPUT);
}

void loop() {
  digitalWrite(LED_BUILTIN, HIGH);
  delay(100);
  digitalWrite(LED_BUILTIN, LOW);
  delay(100);
}
				
			

Debug mit Serial

Um zu überwachen, was unser Arduino tut gibt es verschiedene Optionen. Eine sehr einfache ist die Ausgabe von Daten über den Serial-Port. Diese Daten können wir am Computer über den Serial Monitor anzeigen. Der Serial Monitor wird mit dem Stecker-Icon unten links gestartet.

Der folgende Sketch zählt in der Loop immer weiter nach oben und gibt den Wert über Serial aus. Dazu wird zuerst eine Variable vom Typ “int” mit dem Name “tick” definiert. Im Setup wird die Geschwindigkeit der Serial-Kommunikation auf 9600 Baud festgelegt. In der Loop wird dann tick immer weiter hochgezählt und über Serial an den Computer gesendet.

				
					#include <Arduino.h>

int tick = 0;

void setup() {
    Serial.begin(9600);
}

void loop() {
    Serial.println(tick);
    tick++;    
}
				
			

Ampel

Als drittes Beispiel programmieren wir eine kleine Ampel. Neben der Programmierung kommen hier noch einige elektronische Komponenten ins Spiel. Für das Beispiel werden Vorwiderstände und LEDs in drei Farben benötigt. Aufgebaut wird alles auf einem Breadboard.

Der Code ist eine sehr einfache Variante einer Ampel. Mithilfe des Befehl “delay” wird die Ausführung pausiert und die Ampelphasen nach einer kurzen Wartezeit umgeschaltet.

				
					#include <Arduino.h>

#define D3  3
#define D4  4
#define D5  5

void setup() {
    Serial.begin(9600);

    pinMode( D3, OUTPUT);
    pinMode( D4, OUTPUT);
    pinMode( D5, OUTPUT);
}

void loop() {

    digitalWrite( D3, HIGH);
    digitalWrite( D4, LOW);
    digitalWrite( D5, LOW);

    delay(5000);

    digitalWrite( D3, HIGH);
    digitalWrite( D4, HIGH);
    digitalWrite( D5, LOW);

    delay(2000);

    digitalWrite( D3, LOW);
    digitalWrite( D4, LOW);
    digitalWrite( D5, HIGH);

    delay(10000);

    digitalWrite( D3, LOW);
    digitalWrite( D4, HIGH);
    digitalWrite( D5, LOW);

    delay(2000);

    digitalWrite( D3, HIGH);
    digitalWrite( D4, LOW);
    digitalWrite( D5, LOW);

    delay(10000);
}
				
			

Abstandssensor mit Servo

Über PlatformIO lassen sich Bibliotheken installieren, auf die wir in unserem Projekt zurück greifen können. Dazu müssen wir auf PlatformIO Home gehen (das kleine Haus-Icon unten links) und dort auf den Punkt “libraries” wechseln. Dort können wir dann in die Suche das Wort Servo eintragen. Unter den Treffern sollte, recht weit oben, die Bibliothek von Michael Margolis sein. Auf diese können wir klicken.

Über den Button “Add to project” können wir diese Bibliothek zu unserem Projekt hinzufügen. Außerdem können wir uns direkt den Beispielcode aus der Bibliothek kopieren und lernen, wie wir damit arbeiten können.

In diesem Code hier, sind ein Ultraschall-Abstandssensor und ein Servo gekoppelt. Das Servo wird bewegt, wenn wir dem Sensor näher kommen.

				
					#include <Arduino.h>
#include <Servo.h>

#define PIN_TRIGGER 7
#define PIN_ECHO 6
#define PIN_SERVO 9

Servo myservo;
int pos = 0;
long dauer = 0;
long entfernung = 0;

void setup()
{
  Serial.begin(9600);

  myservo.attach(9);
  pinMode(PIN_TRIGGER, OUTPUT);
  pinMode(PIN_ECHO, INPUT);
}

void loop()
{

  digitalWrite(PIN_TRIGGER, LOW);
  delay(5);
  digitalWrite(PIN_TRIGGER, HIGH);
  delay(10);
  digitalWrite(PIN_TRIGGER, LOW);
  dauer = pulseIn(PIN_ECHO, HIGH);
  entfernung = (dauer / 2) * 0.03432;
  if (entfernung >= 500 || entfernung <= 0)
  {
    Serial.println("Kein Messwert");
  }
  else
  {
    Serial.print(entfernung);
    Serial.println(" cm");
    pos = map(entfernung, 5, 20, 0, 180);
    myservo.write(pos);
  }
  delay(150);
}
				
			
				
					#include <Arduino.h>
#include <Servo.h>

Servo karl;

void setAmpel(uint8_t green, uint8_t yellow, uint8_t red) {
  digitalWrite(11, green);
  digitalWrite(12, yellow);
  digitalWrite(13, red);
}


// C++ code
//
void setup()
{
  pinMode(11, OUTPUT);
  pinMode(12, OUTPUT);
  pinMode(13, OUTPUT);

  karl.attach(5);
}


int last = 0;
int pos = 0;
int dir = 1;

void loop()
{
  karl.write(pos);
  pos += dir;
  dir = pos < 1 || pos > 179 ? dir * -1 : dir;
  delay(10);

  setAmpel(HIGH, LOW, LOW);

  if (last < millis() - 2000) {
    setAmpel(HIGH, HIGH, LOW);
  }
  if (last < millis() - 3000) {
    setAmpel(LOW, LOW, HIGH);
  }
  if (last < millis() - 5000) {
    setAmpel(LOW, HIGH, LOW);
  }
  if (last < millis() - 6000) {
    last = millis();
  }


}
				
			

Servo steuern über Serial und Button auf D2

				
					#include <Arduino.h>
#include <Servo.h>

Servo myservo;
int pos = 0;

void setup()
{
  Serial.begin(9600);
  myservo.attach(9);
  myservo.write(90);

  pinMode(2, INPUT_PULLUP);
}

int run = 1;
int incomingByte = 0;
int lastButtonPressed = 0;

void loop()
{
  if (digitalRead(2) == LOW)
  {
    if (lastButtonPressed < millis() - 100)
    {
      lastButtonPressed = millis();
      myservo.write(90);
    }
  }

  if (Serial.available() > 0)
  {
    incomingByte = Serial.read();

    if (incomingByte == 108)
    {
      myservo.write(120); // tell servo to go to position in variable 'pos'
    }

    if (incomingByte == 114)
    {
      myservo.write(70); // tell servo to go to position in variable 'pos'
    }

    if (incomingByte == 48)
    {
      myservo.write(90); // tell servo to go to position in variable 'pos'
    }
  }
}
				
			

LEDs und Servo über Serial kontrollieren

				
					#include <Arduino.h>
#include <Servo.h>

#define pin_r 4
#define pin_y 3
#define pin_g 2

Servo myservo;
int servo_states[] = {0, 90, 180};

void setup() {

  Serial.begin(9600);
  myservo.attach(5);

  pinMode(pin_r, OUTPUT);
  pinMode(pin_y, OUTPUT);
  pinMode(pin_g, OUTPUT);

  Serial.println("Hallo Welt");
}

int state_r = 0;
int state_y = 0;
int state_g = 0;
int state_servo = 1;

void loop() {

  if (Serial.available() > 0) {
    int incomingByte = Serial.read();

    if (incomingByte == 103) {
      state_g = (state_g + 1) % 2;
    }
    if (incomingByte == 121) {
      state_y = (state_y + 1) % 2;
    }
    if (incomingByte == 114) {
      state_r = (state_r + 1) % 2;
    }

    // DAS IST DAS SERVO
    if (incomingByte == 115) {
      state_servo = (state_servo + 1) % 3;
    }

    if (incomingByte == 116) {
      state_r = (state_r + 1) % 2;
      state_y = (state_y + 1) % 2;
      state_g = (state_g + 1) % 2;
    }
  }

  digitalWrite(pin_r, state_r);
  digitalWrite(pin_y, state_y);
  digitalWrite(pin_g, state_g);
  myservo.write(servo_states[state_servo]);
}
				
			

Von Joni, Mai 2019