Gleisbesetztmeldung mit HC-SR04

März 2020. Nachdem mich niemand zur großen Corona Party eingeladen hat, bleib ich halt zhaus und spiele mit der Eisenbahn und dem Arduino herum. Dabei ist mir die Idee gekommen, alternativ zur Infrarot- Gleisbesetztmeldung auch amal einen Ultraschall- Melder zu probieren.

Die schon im Internet kursierenden Sketches waren ganz ok, jedoch hätten die pro Sensor einen Arduino verbraucht und die arbeiteten durchwegs mit delay(). Wie wir mittlerweile wissen, bringt delay() den ganzen Arduino zum Stillstand, was für allfällige Parallelverarbeitungen das Aus bedeutet.

 

Merke

Ein HC-SR04 ist gut!

Mehrere HC-SR04 sind noch viel besser!

Das gilt natürlich vermehrt noch für ColaRot Glasln !!!

 

Also was liegt näher, dieses Manko programmiertechnisch zu beseitigen. Und noch besser, das ganze Zeugl generisch zu machen, damit ma vielleicht mehrere HC-SR04 ohne weiß Gott welche Änderungen dazufummeln kann.

Ich muß jetzt ehrlicherweise eingestehen, daß ich nicht so der oberüberdrübersupermegaobjektorientierter Programmierer bin, also könnt der Code noch bissl besser ausschauen - mit diversen Funktionen und Unterprogrammen, aber er funkt und das Prinzip ist auch klar. Wer mag, kann ihn ja noch optimieren.

 

Und so schaut der Testaufbau aus, die HC-SR04 haben am Steckbrett die optimale Höhe.

 

Und so schaut die Geschichte aus, ein beliebiger Arduino - ich hab einen chinesischen Nano genommen - man braucht je Meßstation (also SR04 + 2 LEDs) 4 I/O Pins, somit ist alles mit 12 I/Os voll ok. Wer optional noch eine Anzeige per LCD 1602 haben will, braucht halt 5 I/Os mehr. Ich hab für mein 1602 Modul die Analogpins genommen, die brauch ich dafür eh net. Hier mal die Basisversion ohne LCD:

 

Code

/*
   Track occupied Indicator for model railway

  provided from www.kraweuschuasta.at without any warranty or license 2020
  for personal use only
   based on Guide for Ultrasonic Sensor HC-SR04
   created by Rui Santos, https://randomnerdtutorials.com

   One HC-SR04 Sensor one red LED and one green LED per Track
   A red and a green LED show if track is free or occupied (Arduino LOW = LED ON)
   optional: LC Display type 1602

    For better detectance of Tracks place HC-SR04 looking approx. 15 degrees up above track.
             / 15 Degrees up
            /
    SR1504 /   |track|

    Indicator LEDS
    VCC <--1k--redLED----- Adruino Pin
    VCC <--1k--greenLED--- Adruino Pin

    !!! During first use check serial monitor if setup of maxdistance and detected value intcurrentdistance fit !!!

    Explanation serial Monitor output
    First Number      Track Number or SR04 Number (beginning from 0)
    Second number:    detected distance (important to adjust maxdistance[]
    Text              Track occupied/free
    1  7    Track free
    0  4    Track occupied
    1  7    Track free
    0  4    Track occupied


*/
// INIT section ============
byte btTriggerpin[] = {3, 8};        // Array for HC-SR04 Triggerpins                 {SR04_01_TriggerPin,SR04_02_TriggerPin}
byte btEchopin[] = {4, 9};           // Array for HC-SR04 Echopins                    {SR04_01_EchoPin,SR04_02_EchoPin}
byte btRedLEDpin[] = {5, 10};        // Array for Led when track occupied LOW=ON      {red Led for SR04_01,red Led for SR04_02}
byte btGreenLEDpin[] = {6, 11};      // Array for Led when track free LOW=ON          {green Led for SR04_01,green Led for SR04_02}
const int maxdistance[] = {5, 5};    // Array for HC-SR04 max distance to wall [cm]   {max Distance to wall for SR04_01,max Distance to wall for SR04_02}
const int maxdeviation[] = {0, 0};   // Array for HC-SR04 deviation by sens err [cm]  {max deviation for SR04_01,max deviation for SR04_02}
const long interval_LCD = 300;       // Display refresh interval (if used)

//enable next 2 lines if  LCD is used
//#include <LiquidCrystal.h>
//LiquidCrystal lcd(A5, A4, A3, A2, A1, A0); // Pins for LC Display, here I use AD pins as they are not needed for Sensors
// INIT section END ========

long lastmillis_sensor[(sizeof(btTriggerpin) / sizeof(btTriggerpin[0]))];
int duration[(sizeof(btTriggerpin) / sizeof(btTriggerpin[0]))];
int intCurrentDistance[(sizeof(btTriggerpin) / sizeof(btTriggerpin[0]))];
long lastmillis_LCD = millis();
String strbesetzt, strfrei = "";

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

  //enable next 4 lines if LCD is used
  //  lcd.begin(16, 2);
  //  lcd.setCursor(0, 0);
  //  lcd.print("Initializing...");

  for (byte i = 0; i < (sizeof(btTriggerpin) / sizeof(btTriggerpin[0])); i++) {

    lastmillis_sensor[i] = millis();
    //Define inputs and outputs
    pinMode(btTriggerpin[i], OUTPUT);
    pinMode(btEchopin[i], INPUT);
    pinMode(btGreenLEDpin[i], OUTPUT);
    pinMode(btRedLEDpin[i], OUTPUT);
  }

  //enable  line if  LCD is used
  //delay(2000); // delay for displaying init section

  Serial.println("Setup done ======");
}

void loop() {
  strbesetzt = "";
  strfrei = "";

  for (byte i = 0; i < (sizeof(btTriggerpin) / sizeof(btTriggerpin[0])); i++) {

    // The sensor is triggered by a HIGH pulse of 10 or more microlastmillis_sensor.
    // Give a short LOW pulse beforehand to ensure a clean HIGH pulse:
    digitalWrite(btTriggerpin[i], LOW);
    delayMicroseconds(5);
    digitalWrite(btTriggerpin[i], HIGH);
    delayMicroseconds(10);
    digitalWrite(btTriggerpin[i], LOW);
    // Read the signal from the sensor: a HIGH pulse whose
    // duration is the time (in microlastmillis_sensor) from the sending
    // of the ping to the reception of its echo off of an object.

    duration[i] = pulseIn(btEchopin[i], HIGH);

    // Convert the time into a distance
    intCurrentDistance[i] = (duration[i] / 2) / 29.1;   // Divide by 29.1 or multiply by 0.0343
    Serial.print(String(i) + "  " + String(intCurrentDistance[i]));

    if (intCurrentDistance[i] <= maxdistance[i]) { //check if distance smaller than maxdistance minus maxdeviation

      strbesetzt = strbesetzt + String(i);   //generate list of occupied Tracks
      digitalWrite(btRedLEDpin[i], LOW);
      digitalWrite(btGreenLEDpin[i], HIGH);
      Serial.println("    Track occupied");

    } else {
      if (intCurrentDistance[i] <= (2 * maxdistance[i])) { // ignore values above 2double of the max values (prevent Sensor errors)

        strfrei = strfrei + String(i);   //generate list of free Tracks
        digitalWrite(btRedLEDpin[i], HIGH);
        digitalWrite(btGreenLEDpin[i], LOW);
        Serial.println("    Track free");
      }

    }
    // LCD Output here - enable the if clause if LCD is used

    //    if ((lastmillis_LCD + interval_LCD) < millis()) {
    //      lcd.clear();
    //      lcd.setCursor(0, 1);
    //      lcd.print("Belegt:");
    //      lcd.setCursor(8, 1);
    //      lcd.print(strbesetzt);
    //      lcd.setCursor(0, 0);
    //      lcd.print("Frei: ");
    //      lcd.setCursor(8, 0);
    //      lcd.print(strfrei);
    //      lastmillis_LCD = millis();
    //    }


  }
}

 

Das wars schon, Sketch auf den Arduino geschossen und los geht's.

Unterhalb ein Video, wies funktioniert. Viel Spaß!

 

Tip: Serial Monitor

Zum Einrichten empfehle ich den Serial Monitor anzuschauen, der gibt auch die gemessenen Distanzen aus. Damit tut man sich wesentlich leichter, die Länge(n) zu parameterisieren.

 

Anfangs haben die HC-SR04 bei kurzen Distanzen absoluten Nonsens angezeigt, wenn mas aber um ca. 15 Grad hochkippt, lieferns meist ganz ordentliche Werte.

 

Fazit

Überzeugt nicht zu 100%.

Vorteile: Einfache Schaltung, Der Ultraschallsensor kann gegen eine Wand gerichtet werden und braucht keinen elektronischen "Gegenpart".

Nachteile: die Einrichtung ist eine ziemliche Spielerei, Die Messwerte sind nicht immer exakt, immerwieder kommen extreme Ausreißer, die man in der Software abfangen muss.

 

zurück zur Eisenbahnelektronik