#include <NewPing.h>             //fuer Ultraschall-Sensor
#include <Wire.h>                //fuer I2C Kommunikation  
#include <Adafruit_GFX.h>        //fuer OLED
#include <Adafruit_SSD1306.h>    //fuer OLED

#define LEVELFULL  31 //Hoehe Wassersaeule wenn voll (=100%) in cm
#define LEVELEMPTY 44 //Abstand Boden zu Sensor in cm
#define VOLUME 5   //Volumen des zu messenden Mediums bei 100% in l
#define OLED_ADDRESS 0x3C // I2C-Adresse des Displays 0x3C, 0x3D, .... (siehe Datenblatt)
#define OLED_RESET     4  // Reset pin
#define SCREEN_WIDTH 128  // OLED Display Breite in Pixel
#define SCREEN_HEIGHT 64  // OLED Display Hoehe in Pixel
#define trigPin  2        // Trigger Pin
#define echoPin  3        // Echo Pin
#define MAX_DISTANCE 320  // Begrenzung fuer Ultraschallsensor
#define analogPin 5       // Pin für den Analogausgang

float duration;
int lefty, distance, levelact, levelpercent, volumeact, volumeanalog;

Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);

NewPing sonar(trigPin, echoPin, MAX_DISTANCE); // NewPing Setup

void setup() {
  Serial.begin(9600); // nur fuer Debugzwecke

  // SSD1306_SWITCHCAPVCC = generate display voltage from 3.3V internally
  if(!display.begin(SSD1306_SWITCHCAPVCC, OLED_ADDRESS)) { 
    Serial.println(F("SSD1306 allocation failed"));
    for(;;); // Endlosschleife
  }  
}

void loop() {
  delay(50); // Wartezeit zwischen Pings (ca. 20 pings/sec). nicht kleiner als 29ms! 
  
  //Messung mit Mittelung
  int iterations = 5;
  duration = sonar.ping_median(iterations);      //duration = sonar.ping() ohne Mittelung
  distance = int((duration / 2) * 0.0343);       //Abstand in cm

  //Fuellstand berechnen
  levelact = (LEVELEMPTY - distance);

  //Fuellstand in Prozent berechnen
  levelpercent = (levelact * 100 / LEVELFULL);
  
  //Berechnung Volumen
  volumeact = (VOLUME * levelpercent);

  //Berechnung Analogwert
  volumeanalog = ((VOLUME * levelpercent) / 2);

 if(levelpercent >= 99)
 //Ausgabe 10V wenn Behälter voll
   analogWrite (analogPin, 255);
 
 else 
  //Ausgabe des Analogwertes
  analogWrite (analogPin, volumeanalog);

  display.clearDisplay(); // Clear display buffer

  //Ueberschrift
  display.setTextSize(2);      // Schriftgroesse
  display.setTextColor(WHITE); // Textfarbe
  display.setCursor(0, 0);     // obere linke Ecke
  display.print("Levelmeter");
  
  //Fuellstand in Prozent
  display.setTextSize(2);      // Schriftgroesse
  display.setTextColor(WHITE); // Textfarbe
  display.setCursor(50, 24);   // obere linke Ecke
  display.print(levelpercent);
  display.print(" %");
  
  //Fuellstand in Litern
  display.setTextSize(2);
  display.setCursor(50, 48);
  display.print(volumeact);
  display.print(" l");  
  
  //Balkenanzeige
  levelpercent = constrain(levelpercent, 0, 100);  //um nur Werte zwischen 0 und 100 zu erhalten
  lefty = map(levelpercent, 0, 100, 63, 16);       //Hoehe des Balkens ermitteln
  
  //aeusserer Rahmen zeichnen
  display.drawRect(0, 16, 38, 48, WHITE);
  
  //Balken zeichnen
  display.fillRect(0, lefty, 38, 48, WHITE);
  
  //Display anzeigen
  display.display();
}