5. 🚧 Hardware trifft Software mit Processing

Synopsis: [ 🔗 https://processing.org] [🔗 Processing Cheat Sheet]

Einstieg mit Poti auslesen

Der Poti auf dem Expansion Board wird ausgelesen und als zwei Byte über die Serielle Schnittstelle verschickt.

#include <Wire.h> // Wire Bibliothek einbinden
#include <LiquidCrystal_PCF8574.h>
LiquidCrystal_PCF8574 lcd (0x27); // LCD-Adresse auf 0x27 für 16 Zeichen und 2 Zeilen ein

void setup() {
   lcd.begin(16, 2); // initialize the lcd
   lcd.clear();
   lcd.setBacklight(255);
   lcd.setCursor(0,0); // erstes Zeichen, erste Zeile
   lcd.print("UART Poti");
   Serial.begin(9600);
}

void loop() { 
  uint16_t potiVal = analogRead(PA0);
  unsigned char byte_0 = potiVal & 0x00FF; // left Byte;
  unsigned char byte_1 = (potiVal & 0xFF00) >> 8;  // right Byte;
  lcd.setCursor(0,1); // erstes Zeichen, zweite Zeile
  lcd.printf("%X %X", byte_1, byte_0);
  Serial.write(byte_1);
  Serial.write(byte_0);
  delay(1000);
}

Mit Processing werden die Daten empfangen und angezeigt

import processing.serial.*;   //verwende die 'Serial' Library
 
Serial myPort;   // Deklariere ein Object vom Typ 'Serial', mit dem Namen 'myPort'
//globale Variablen
int x = 0;
//stelle eine Verbindung zum Arduino/Mbed Controller her.
void setup() {
  size(600,600); // Zeichenfläche
  background(#FFFFFF); // Weisser Hintergrund
  ellipseMode(CENTER); // Tentrum der Ellipse als Referenzpunkt
  
  // Setup der Kommunikation über UART
  // ======================================================
  // Um den Namen der Schnittstelle herauszufinden, lassen wir uns von der Serial-Klasse zunächst
  // eine Liste der Namen der am PC vorhandenen Ports geben ('Serial.list()')
  // Den x'ten Eintrag aus dieser 
  //Liste bekommen wir mit 'Serial.list()[x-1]' (die indices fangen mit 0 an)
  // Bei den meisten PCs ist der STM32/Arduino der letzte Port in der Liste.
  // Deshalb ermitteln wir zunächst die Länge der Liste (Serial.list().length) 
  // und nehmen uns den letzten Namen aus der Liste heraus (Serial.list()[Serial.list().length-1]) 
  String portName = Serial.list()[Serial.list().length-1]; // finde den Namen des letzten Serialports heraus.
  println(portName);  //print them to the user
  int baudrate=9600;       // Diese Baudrate muss mit der in eurem MBed/Arduino-Programm übereinstimmen.
  // portName = "COM1";        // Feslegung auf Port COM1 sonst wird die erste Schnittstelle in der Liste verwendet.
  myPort = new Serial(this, portName, baudrate); // Erzeuge ein Serial Objekt und stelle eine Verbindung her
  
}
 
//Lese Daten und gib sie in Processing aus
void draw() {
  int  int_16bit = 0;
  while (myPort.available() > 0) { // wiederhole das folgende, solange (while) es neue Daten (myPort.available() > 0)gibt.
    //char inByte = myPort.readChar(); // lese einen einzelnen Buchstaben von der Seriellen Schnittstelle
    //int intByte = myPort.read();
    //println(hex(inByte)); // gib den gelesenen Buchstaben im Fenster unter dem Code aus.
    //if(inByte == '\n') println();
    //print(intByte);
    //int int_16bit =  (myPort.readChar()<<8)+myPort.readChar(); 
    //println(hex(int_16bit));
    char hByte = myPort.readChar();
    char lByte = myPort.readChar();
    int_16bit = (hByte<<8) + lByte;
    print("0x"+hex(int_16bit,4)+" "+int_16bit+" ");
    println(int_16bit*(3.3/65535)+" V");
    stroke(1);
    //point(x,int_16bit/100);
    fill(#EA0707);
    ellipse(x,200-int_16bit/10,5,5);
    x += 5;
    if(x==width) {
      x=0;
      background(#FFFFFF);
    }
  }
}

Processing Sonar

Synopsis: [🔗 https://www.youtube.com/watch?v=uO3mTJZ2Vyc ]

Servo an Expansion Board anschließen, Ultraschallsensor an PA10 und PC9.

#include <Wire.h> // Wire Bibliothek einbinden
#include <LiquidCrystal_PCF8574.h>
LiquidCrystal_PCF8574 lcd (0x27); // LCD-Adresse auf 0x27 für 16 Zeichen und 2 Zeilen ein
#include <Servo.h>
Servo myservo;
#define TRIGGER PA10 // Anschluss Ultraschall-Entfernungsmesser
#define ECHO PC9

void setup() {
  lcd.begin(16, 2); // initialize the lcd
  lcd.clear();
  lcd.setBacklight(255);
  lcd.setCursor(0,0); // erstes Zeichen, erste Zeile
  lcd.print("Sonar Demo");
  Serial.begin(115200); 
  pinMode(PC1,OUTPUT);    // PC1 braucht +5V
  digitalWrite(PC1,HIGH);
  myservo.attach(PC0);   // Servosignal an PC0 anschliessen
  pinMode(TRIGGER, OUTPUT); // Trigger-Pin ist ein Ausgang
  pinMode(ECHO, INPUT);     // Echo-Pin ist ein Eingang
}
int minGrad = 0;
int maxGrad = 180;
int grad = 0;
bool nachRechts = true;

void loop() { 
  unsigned int mess;
  digitalWrite(TRIGGER, HIGH); // Trigger-Pin high zum Start der Messung
  delayMicroseconds(10);       // 10 µs reichen 
  digitalWrite(TRIGGER, LOW);  // Trigger-Pin low
  mess = pulseIn(ECHO, HIGH,12000); // auf 12ms begrenzen für max 206cm
  mess = mess/58; // näherungsweise Berechnung der cm 
  myservo.write(grad);
  lcd.setCursor(0,1); // erstes Zeichen, zweite Zeile
  lcd.printf("%3d %3d", mess, grad);
  Serial.write(mess & 0xFF);
  Serial.write(grad & 0xFF);
  if (nachRechts) {
    grad += 3;
    if (grad > maxGrad) {
      grad = maxGrad;
      nachRechts = false;
    }
  }
  else {
    grad -= 3;
    if (grad < minGrad) {
      grad = minGrad;
      nachRechts = true;
    }
  }
  delay(100);
}

Mit Processing die Daten visualisieren

import processing.serial.*; // Serial-Bibliothek
import processing.sound.*; // Sound-Bibliothek

Serial meinPort; // serieller Port
SoundFile file; // Sounddatei
float radius = 500; // Setzt den Radius
boolean richtung = true; // Setzt die Richtung
int gradZahl = 0; 
int entfernung = 0;
ArrayList<Linie> linien = new ArrayList<Linie>(); // Erstellt einen Array von Linien
int letzteAbspielzeit = 0;

void setup() {
  serialPortEinrichten(); // Ruft die Funktion zum Einrichten des seriellen Ports auf
  size (1000, 500); // Größe des Fensters
  background(#050505); // Hintergrundfarbe
  file = new SoundFile(this, "sound/sonarPing.mp3"); // Lädt die Sounddatei
}

void draw() {
  datenLesenUndVerarbeiten(); // Ruft die Funktion zum Lesen und Verarbeiten der Daten auf
  bildZeichnen(); // Ruft die Funktion zum Zeichnen des Bildes auf
}

// Funktion zum Einrichten des seriellen Ports
void serialPortEinrichten() {
  String portName = Serial.list()[Serial.list().length-1]; // Holt den Portnamen
  println(portName); // Zeigt den Portnamen an
  int baudrate=115200; // Setzt die Baudrate
  //portName = "COM3"; // Setzt den Portnamen falls nicht der erste in der Liste
  meinPort = new Serial(this, portName, baudrate); // Öffnet den seriellen Port
}

// Funktion zum Lesen und Verarbeiten der Daten
void datenLesenUndVerarbeiten() { 
  while (meinPort.available() >= 2) { // Während Daten verfügbar sind
    byte[] inBuffer = new byte[2]; // Erstellt einen Puffer
    meinPort.readBytes(inBuffer); // Liest die Bytes
    
    // Setzt die geteilten Werte wieder zusammen
    entfernung = (int) (inBuffer[0] & 0xFF);
    gradZahl = (int) (inBuffer[1] & 0xFF);
    
    println("Entfernung: " + entfernung + ", Grad: " + gradZahl); // Zeigt die Entfernung und Gradzahl an
    linien.add(new Linie(entfernung, gradZahl)); // Fügt eine neue Linie hinzu
    
    // Wiederholt den sound je nach Abstand unterschiedlich
    if (entfernung <= 50 && millis() - letzteAbspielzeit > 3000) { 
      file.play(); // Spielt die Datei ab
      letzteAbspielzeit = millis(); // Aktualisiert die letzte Abspielzeit
    }
    else if (entfernung <= 30 && millis() - letzteAbspielzeit > 2000) { 
      file.play(); // Spielt die Datei ab
      letzteAbspielzeit = millis(); // Aktualisiert die letzte Abspielzeit
    }
    else if (entfernung <= 10 && millis() - letzteAbspielzeit > 1000) {
      file.play(); // Spielt die Datei ab
      letzteAbspielzeit = millis(); // Aktualisiert die letzte Abspielzeit
    }
  }
}

// Funktion zum Zeichnen des Bildes
void bildZeichnen() {
  size (1000, 500); // Größe des Fensters
  background(#050505); // Hintergrundfarbe
  halbkreiseZeichnen(); // Ruft die Funktion zum Zeichnen der Halbkreise auf
  rasterZeichnen(); // Ruft die Funktion zum Zeichnen des Rasters auf
  for (Linie l : linien) { // Für jede Linie in dem Array
    l.linieZeichnen(); // Zeichnet die Linie
  }
  linien.removeIf(l -> l.istAnzeigedauerAbgelaufen()); // Entfernt abgelaufene Linien

  textSize(20); // Textgröße
  textAlign(CENTER, CENTER); // Textausrichtung
  
  // Textfarbe je nach Entfernung
  if (entfernung <= 50) {
    fill(#FF0000); // Füllfarbe
    text("Entfernung:\n" + entfernung + " cm", 50, 20); // Zeichnet den Text
  }
  else if (entfernung > 50) {
    fill(#28BF00); // Füllfarbe
    text("Entfernung:\n> 50 cm", 50, 20); // Zeichnet den Text
  }
  fill(#28BF00); // Füllfarbe
  text("Winkelgrad:\n" + gradZahl + "°", width - 50, 20); // Zeichnet den Text
}

// Funktion zum Zeichnen der Halbkreise
void halbkreiseZeichnen() { 
  fill(#001504); // Füllfarbe
  stroke(#218320); // Strichfarbe
  strokeWeight(8); // Strichstärke
  ellipse(500,500,1000,1000); // Zeichnet einen Kreis
   
  fill(#28BF00); // Füllfarbe
  ellipse(500,500,50,50); // Zeichnet einen Kreis
   
  noFill(); // Keine Füllung
  stroke(#218320); // Strichfarbe
  strokeWeight(4); // Strichstärke
  ellipse(500,500,200,200); // Zeichnet einen Kreis
   
  noFill(); // Keine Füllung
  stroke(#218320); // Strichfarbe
  strokeWeight(5); // Strichstärke
  ellipse(500,500,400,400); // Zeichnet einen Kreis
   
  noFill(); // Keine Füllung
  stroke(#218320); // Strichfarbe
  strokeWeight(6); // Strichstärke
  ellipse(500,500,600,600); // Zeichnet einen Kreis
   
  noFill(); // Keine Füllung
  stroke(#218320); // Strichfarbe
  strokeWeight(7); // Strichstärke
  ellipse(500,500,800,800); // Zeichnet einen Kreis

  // Skalierung hinzufügen
  textSize(12); // Textgröße
  fill(#28BF00); // Füllfarbe
  textAlign(CENTER, CENTER); // Textausrichtung
  for (int i = 1; i <= 5; i++) {
    text(i*10 + "cm", 500, 500 - i*100 + 10); // Zeichnet den Text
  }
}

// Funktion zum Zeichnen des Rasters
void rasterZeichnen() { 
  noFill(); // Keine Füllung
  stroke(#28BF00); // Strichfarbe
  strokeWeight(0.2); // Strichstärke
  for (int i = 0; i < 10; i++) {
    for (int j = 0; j < 5; j++) {
      rect(i*100, j*100, 100, 100); // Zeichnet ein Rechteck
    }
  }
}

// Klasse Linie zum erzeugen einer Linie als Objekt
class Linie { 
  float entfernung; 
  int gradZahl; 
  int anzeigedauer = 180; // Setzt die Anzeigedauer
  float x_Position;
  float y_Position; 

  // Konstruktor
  Linie(int entfernung, int gradZahl) { 
    this.entfernung = entfernung; // Setzt die Entfernung
    this.gradZahl = gradZahl; // Setzt die Gradzahl
    float rad = radians(gradZahl); // Berechnet den Winkel
    this.x_Position = 500 + radius * cos(rad); // Berechnet die X-Position
    this.y_Position = 500 - radius * sin(rad); // Berechnet die Y-Position
  }

  // Funktion zum Zeichnen der Linie
  void linieZeichnen() { 
    float rad = radians(gradZahl); // Berechnet den Winkel
    float skalierteEntfernung = map(entfernung, 0, 50, 0, 500); // Skaliert die Entfernung von 0 bis 50 auf 0 bis 500
    skalierteEntfernung = min(skalierteEntfernung, radius); // Skaliert die Entfernung
    float x_Linie = 500 + skalierteEntfernung * cos(rad); // Berechnet die X-Position der Linie
    float y_Linie = 500 - skalierteEntfernung * sin(rad); // Berechnet die Y-Position der Linie
    strokeWeight(5); // Setzt die Strichstärke
    
    int transparenz = (int) map(anzeigedauer, 0, 360, 0, 255); // Berechnet die Transparenz
    stroke(#28BF00, transparenz); // Strichfarbe und Transparenz
    line(500, 500, x_Linie, y_Linie); // Zeichnet die Linie
    if (entfernung <= 50) {
      stroke(#FF0000, transparenz); // Strichfarbe und Transparenz
      line(x_Linie, y_Linie, x_Position, y_Position); // Zeichnet die Linie
    }
    anzeigedauer--; // Verringert die Anzeigedauer
  }
  
  // Funktion zum Überprüfen, ob die Anzeigedauer abgelaufen ist
  boolean istAnzeigedauerAbgelaufen() { 
    return anzeigedauer <= 0; // Gibt true zurück, wenn die Anzeigedauer abgelaufen ist
  }
}