Anfänger braucht Hilfe mit uno

Hallo zusammen,

muss erstmal sagen das ich blutiger Anfänger bin und mir einen Arduino uno mit zweizeiligem Display zugelegt habe weil ich tolle Dinge im Youtube gesehen habe die von Usern gebastelt wurden.

Habe folgende Idee, würde gerne eine Zeitmessung für meine Slotbahn programmieren die dann im nächsten Step grafisch auf einem PC dargestellt werden kann.
Stelle mir das so vor....jede Spur wird mit einem Reed Schalter ausgestattet und löst die Zeitmessung für die jeweilige Spur über einen Digitaleingang aus.

Das könnte Display 2x16 so aussehen:

1: 7:12 SR 6:05
2: 8:34 SR 7:23

1: für Slot 1 und die aktuwelle Rundenzeit und SR für die schnellste Rundenzeit.

wäre wirklich für jede verständliche Hilfe dankbar

Hilfe bei was? Aufbau, Programmierung?

ich denke der aufbau ist kein Problem, eher die Programmierung

zum Aufbau, wollte eben mit zwei Reed kontakten in die digitalen eingänge gehen das sollte soweit kein Problem sein denke ich.

Was für ein Display hast du den ?
Die Idee ist gut , hast du die Sache schon
gebaut?

ja soweit alles fertig gebaut, mir fällt nur gerade auf das ich ja keine digitale eingänge habe sondern nur analoge oder?

habe das das zweizeilige lcd shield von sainsmart

http://www.sainsonic.com/zen/albums/ebay/sainsmart/20-011-805%202560+V5+XBee+LCD/IMG_2210.jpg

habe mal den link von dem display eingefügt :wink:

Wie sieht denn dein bisheriger Sketch aus? Wo hängt es denn genau? Oder soll hier jemand die komplette Programmierung übernehmen?
Ein paar Threads weiter ging es schon um eine Stoppuhr, vielleicht kannst du dir da ein paar zusätzliche Inspirationen holen.

mediakasper:
wäre wirklich für jede verständliche Hilfe dankbar

Da Slotcars schnell sind und daher nur (wenn überhaupt) extrem kurze Schließzeiten an den Reedkontakten verursachen, kannst Du die beiden Hardwareinterrupts an Pin-2 und Pin-3 (D2, D3) des UNO verwenden, um ein Signal abzugreifen.

Da der UNO nur zwei Hardwareinterrupts hat, wäre "zwei" zugleich auch die maximale Anzahl von überwachbaren Eingängen.

Bevor Du das komplett aufbaust, würde ich allerdings erst mal mit Hilfe eines Test-Sketches, Reedkontakt und der Pin-13 LED auf dem UNO austesten, ob Reedkontakte überhaupt zur Erfassung geeignet sind. Oder bist Du Dir diesbezüglich schon sicher, weil es andere Slotcarbastler auch mit Reedkontakten machen?

Hier mal ein Test-Sketch, wobei ich davon ausgehe, dass der Reedkontakt ein einfacher Schließkontakt ist und zwischen Ground und D2 angeschlossen wird. Beim Auslösen sollte dann die PIN-13 LED auf dem UNO für eine halbe Sekunde angehen, und zwar auch bei extrem hoher Fahrgeschwindigkeit.

#define LEDPIN 13
#define REEDPIN1 2
#define REEDINTERRUPT1 0
long roundStart1=-9999;


void setup() {
  // put your setup code here, to run once:
  pinMode(LEDPIN,OUTPUT);
  pinMode(REEDPIN1,INPUT_PULLUP);
  attachInterrupt(REEDINTERRUPT1, timing1, CHANGE);
}

void timing1()
{
  boolean static lastState;
  boolean state;
  state=digitalRead(REEDPIN1);
  if (state!=lastState && state==LOW)
    roundStart1=millis();
  lastState=state;  
}

void loop()
{
  if (millis()-roundStart1<500)
    digitalWrite(LEDPIN,HIGH);
  else  
    digitalWrite(LEDPIN,LOW);
}

Für das endgültige Programm müßten natürlich noch ggf. prellende Kontakte softwaretechnisch entprellt werdenn, die Rundenzeiten ermittelt und auf dem Display ausgegeben werden. Aber am Blinken der LED im Test-Sketch könntest Du schon mal erkennen, ob der Aufbau mit Reedkontakt überhaupt prinzipiell zur Signalerfassung geeignet und der Reedkontakt schnell genug für Deine Slotcars ist.

ich kenne mich überhaupt nicht aus, weiss auch nicht genau wie ich da anfangen soll.

habe also noch keinerlei anfänge in einem sketch

will aber auch nicht das mir jemand den ganzen sketch vorsagt...will nur einen einstieg. ist es denn richtig das die reed kontakte in die analogen eingänge müssen oder gibt es digitale eingänge?

danke jurs für diesen wirklich sehr verständlichen Beitrag :wink:

habe es gerade getestet und siehe da, es klappt. selbst bei maximaler Geschwindigkeit reagiert der Reed Kontakt und LED 13 sind an.

Noch eine Frage zu den I/O´s....habe gerade gelesen das ich...weil es ja I/O´s sind mit Hilfe des Pin Mode befehls sagen kann ob es eingang oder ausgang ist. warum habe ich dann nur zwei und nicht 12 eingänge?

mediakasper:
ich kenne mich überhaupt nicht aus, weiss auch nicht genau wie ich da anfangen soll.

habe also noch keinerlei anfänge in einem sketch

will aber auch nicht das mir jemand den ganzen sketch vorsagt...will nur einen einstieg. ist es denn richtig das die reed kontakte in die analogen eingänge müssen oder gibt es digitale eingänge?

Ein Reedkontakt ist ein Schalter, der kennt nur "ein" und "aus", das ist ein digitales Signal und das wird digital abgefragt.

Und wenn es um extrem schnelle Signale geht, die auf schnarchlangsamen Geräten ausgegeben werden sollen (LCD-Display löschen dauert z.B. zwei Millisekunden), dann kommt nur eine Abfrage der Eingänge per Interrupt in Frage. Beim Polling der Leitung kann es Dir sonst passieren, dass auf Deiner Rundenzeitanzeige gerade eine langsame Aktualisierung der Anzeige läuft, während gerade der Rennwagen den superkurzen Impuls gibt, und dann bekommt der Wagen für eine Runde keine Rundenzeit oder beim nächsten Durchfahren als Rundenzeit die Zeit für eine Runde angeschrieben in der er tatsächlich zwei Runden gefahren ist.

Ein Polling der Eingänge reihum funktioniert nur dann, wenn die Summe der Zeiten für das Abfragen sämtlicher zu überwachenden Eingänge plus die Summe der Zeiten für die langsamste Aktualisierung der Anzeigen sehr klein ist im Verhältnis zur Auslösezeit, während der Reedschalter Kontakt gibt.

Rechenbeispiel:
Mal angenommen die langsamste Abfrage und Display-Aktualisierung dauert 4 Millisekunden (zwei Leitungen abfragen, Display löschen, zwei Zeilen auf Display ausgeben) und der Reed-Kontakt gibt einen Kontakt, während das Slotcar eine Fahrstrecke von 10 mm zurücklegt, dann darf die Geschwindigkeit bei Polling-Abfrage maximal betragen:
0,01 m / 0,004 s = 2,5 m/s.
D.h. bei Abfrage per Polling statt Interrupt können Dir im ungünstigsten Fall ab einer Fahrgeschwindigkeit von mehr als 2,5 m/s einzelne Rundenimpulse verloren gehen, wenn nämlich gerade das Display aktualisiert wird während der nächste Wagen durchfährt. Dies würde immer dann eintreffen, wenn der nachfolgende Wagen nahezu gleichauf ist, also wenn er weniger als 10mm hinter dem ersten Wagen hängt, dann würde beim Polling nur die Rundenzeit des ersten Wagens ermittelt und die Runde des nur wenige Millimeter hintendran hängenden Fahrzeugs würde komplett unter den Tisch fallen, weil der Kontakt auslöst, während gerade das Display des knapp voranfahrenden Fahrzeugs aktualisiert wird.

Dies vermeidest Du durch die Nutzung von Hardware-Interrupts: Solange der Reedkontakt einen noch so kurzen Kontakt gibt, wird der Impuls korrekt erfaßt, auch wenn das Programm gerade dabei ist, die Anzeigetafel zu aktualisieren. Deshalb die Nutzung von Hardware-Interrupts und deshalb auch der Test bei Maximalgeschwindigkeit.

mediakasper:
habe es gerade getestet und siehe da, es klappt. selbst bei maximaler Geschwindigkeit reagiert der Reed Kontakt und LED 13 sind an.

Na bravo! Dann kannst Du darauf aufbauen!

mediakasper:
Noch eine Frage zu den I/O´s....habe gerade gelesen das ich...weil es ja I/O´s sind mit Hilfe des Pin Mode befehls sagen kann ob es eingang oder ausgang ist. warum habe ich dann nur zwei und nicht 12 eingänge?

Ein UNO hat sogar 20 digitale Ein-/Ausgänge, nämlich 14 digitale und 6 analoge, wobei aber auch die analogen Eingänge wie digitale Eingänge genutzt werden können. Das Problem bei schnellen Timings ist aber: Du hast NUR ZWEI INTERRUPTS auf einem Uno. Und daher kannst Du per Interrupt nur zwei Timings machen.

Mit Polling (nacheinander alle Eingänge abfragen) könntest Du zwar bis zu 20 Fahrzeuge einzeln timen, aber wie oben vorgerechnet: Bereits ab einer Fahrgeschwindigkeit von 2,5 m/s und höher würde bei einer sehr knappen Abfolge der Fahrzeuge immer nur das erste erfaßt werden: Wenn das wenige Millimeter hintendranhängende Fahrzeug folgt, ist ein Polling-Programm nämlich dabei gerade das langsame Display zu aktualisieren, und dann geht der Impuls verloren.

Wenn es nicht so genau drauf ankommt, dass in manchen (seltenen) Fällen eine Runde gar nicht gezählt wird, kannst Du es natürlich per Polling machen. Aber wenn die Fahrzeuge dann mit mehr als 2,5m/s unterwegs sind und sie gleichzeitig ganz knapp nacheinander (also fast zeitgleich) ihre Reedkontakte auslösen, mußt Du mit Rundenverlusten rechnen.

Wie viele Fahrzeuge möchtest Du denn erfassen?
Mehr als zwei, wie es Dein ursprüngliches Posting nahelegt?

Genaugesagt ha der arduino UNO 20 digitale Ein/Ausgänge (Numeriert von 0 bis 19)
Bestimmte pins haben Mehrfachfunktionen:

  • 0 und 1 ist die Serielle Schnittstelle die mit einem USB Adapter verbunden ist und zur programmierung gebraucht wird. Falls sie nicht für die Progrrammierung gebraucht wird kann die serieller Schnittsatelle auch als serielle Schnittstelle für andere Elektronik verwendet werden oder als normale Pins.
  • 2 und 3 sind Interrupteingänge mit bestimmten Funktionen (http://arduino.cc/en/Reference/AttachInterrupt) (diese Funktionen haben nur diese, ansonsten für bestimmte Funktionen können auch andere Pins verwendet werden)
  • 3, 5, 6, 9, 10, und 11 sind PWM-Ausgänge
  • 14 bis 19 sind analoge Eingänge
  • 11,12 und 13 ist die SPI Schnittstelle dazu noch ein weiteres Pin Deiner Wahl um das angesprochene Gerät zu aktivieren.
  • 18 und 19 (A4 und A5) ist die I2C Schnittstelle.

warum habe ich dann nur zwei und nicht 12 eingänge?

Weil eben nur die 2 als Interrupteingänge benutzbar sind.

Grüße Uwe

danke für diese sehr detailreiche erklärung, muss wohl nochmal in ruhe durchlesen um es zu verdeutlichen aber im groben habe ich das nachvollziehen können.

ich habe eine rennbahn mit 2 slots auf der maximal 2 autos zugleich fahren können. Also eine analoge bahn.

deshalb wollte ich auch zwei reedschalter verbauen um jeweils einen in den entsprechenden slot zu verbauen.
Hardwaremässig funktioniert es also schonmal.

jetzt muss nur noch die Programmierung klappen :wink:

mir würde es schon reichen wenn ich das ganze für einen slot also einen eingang habe. kann das dann wohl für den zweiten selber umsetzen.

danke uwefed für die verdeutlichung der kontakte.

also muss ich meinen zweiten reed Kontakt an pin 3 anschliessen bei einem gemeinsamen GND?

mediakasper:
also muss ich meinen zweiten reed Kontakt an pin 3 anschliessen bei einem gemeinsamen GND?

Ja, Reedkontakt-1 zwischen Ground und D2, Reedkontakt-2 zwischen Ground und D3.

Die Interrupts sind wie folgt zu verwenden:
D2 ==> Interrupt 0
D3 ==> Interrupt 1

Bis zu 6 Slotcars könntest Du übrigens mit einem MEGA Board per Interupt timen, siehe: http://arduino.cc/en/Main/arduinoBoardMega

mediakasper:
jetzt muss nur noch die Programmierung klappen

Was hast Du denn als LCD?
Ein "Keypad-Shield" (LCD mit 6 Tasten) zum Aufstecken auf den Arduino?
Ein "I2C LCD" das mit nur Plus, Minus und 2 Leitungen angeschlossen wird?
Oder nur ein "nacktes" LCD mit 16 Anschlüssen, von denen Du etliche selbst verdrahten müßtest?

Was hast Du denn als LCD?

ich habe das aufsteck lcd mit den 6 Tasten, habe auch weiter oben einen link zu dem bild eingefügt.

mediakasper:

Was hast Du denn als LCD?

ich habe das aufsteck lcd mit den 6 Tasten, habe auch weiter oben einen link zu dem bild eingefügt.

Dann hast Du ja wenig zu basteln und brauchst nur die beiden Reedkontakte anschließen.

Du kannst ja mal zusehen, ob Du in der loop-Funktion aus "roundStart1" und einer Hilfsvariablen die Rundenzeiten selbst herausbekommst, wenn sich der Wert von "roundStart1" ändert.

Ich muß jetzt erstmal einkaufen fürs Wochenende und kann mir dann nachher nochmal ein paar Gedanken dazu machen.

wäre nett wenn du mir da nochmal Hilfestellung leisten könntest.

baue jetzt mal auf dem script vom Kollegen jurs auf:

#define LEDPIN 13  /das wirds der pin sein der meine led blinken lässt?!
#define REEDPIN1 2         /der Eingang der als erste von zwei Interrupts an Pin 2 fungiert?!
#define REEDINTERRUPT1 0
long roundStart1=-9999;   verstehe ich noch nicht

denke das ich das ganze zweimal machen muss wegen der zwei eingänge, versuche das mal in einem script einzubauen

void setup() {
  // put your setup code here, to run once:
  pinMode(LEDPIN,OUTPUT);
  pinMode(REEDPIN1,INPUT_PULLUP);
  attachInterrupt(REEDINTERRUPT1, timing1, CHANGE);
}

void timing1()
{
  boolean static lastState;
  boolean state;
  state=digitalRead(REEDPIN1);
  if (state!=lastState && state==LOW)
    roundStart1=millis();
  lastState=state;  
}

void loop()
{
  if (millis()-roundStart1<500)
    digitalWrite(LEDPIN,HIGH);
  else  
    digitalWrite(LEDPIN,LOW);
}

jetzt mal mein abgeändertes Script:

#define LEDPIN 13
#define REEDPIN1 2
#define REEDINTERRUPT1 0
long roundStart1=-9999;

#define LEDPIN 14               /tu jetzt mal so als würde es die LED mit pin 14 geben für den zweiten Reed Kontakt
#define REEDPIN2 3
#define REEDINTERRUPT2 1
long roundStart1=-9999;

mediakasper:
wäre nett wenn du mir da nochmal Hilfestellung leisten könntest.

Ich bin dann mal so nett.

Das mit der Interruptprogrammierung geht natürlich weit über "Anfängerprogrammierung" hinaus. Aber ohne die Nutzung von Interrupts ist es für Deinen Anwendungsfall kaum sinnvoll zu realisieren, wenn man nicht "verschluckte" Runden riskieren möchte. Mit typischer Anfängerprogrammierung ist Dein Anwendungsfall leider nicht perfekt in Software umsetzbar, sondern nur mit fortgeschrittenen Programmiertechniken und Nutzung der Hardware-Interrupts.

Als Goodies außerhalb Deiner Spezifikation habe ich gleich nochmal einen "Rundenzähler" eingebaut und eine Anzeige, die im Zweisekundentakt wechselt zwischen "Anzeige der Rundenzeiten" und "Anzeige der Rundenanzahl".

Vollständig unter Echtbedingungen testen kann ich natürlich nicht, da ich weder die Rennbahn noch die Reedkontakte habe.

Das Timing selbst ist bis hin zur Verarbeitung übrigens auf tausendstel Sekunden genau und erst beim Anzeigen der Zeiten wird auf hundertsel Sekunden gerundet.

Probier's aus, ob Du was damit anfangen kannst!

Damit Du ggf. selbst Änderungen vornehmen kannst, habe ich einige Kommentarzeilen dazugeschrieben und mich bemüht, sprechende Variablennamen zu vergeben, die von selbst erklären, was darin gespeichert ist.

Außerdem habe ich das Programm gleich so angelegt, daß man es mit möglichst geringem Aufwand auf bis zu sechs Slot-Zeitmessungen erweitern könnte, falls man es auf ein MEGA Board (mit 6 Interrupts) umschreiben und erweitern möchte.

/* 
   Slotcar timing by "jurs" for German Arduino forum
   Hardware required: Arduino UNO, LCD keypad shield, 2 reed contacts
*/
#include <LiquidCrystal.h>
LiquidCrystal lcd(8, 9, 4, 5, 6, 7); // Sainsmart keypad LCD

#define LEDPIN 13
#define SLOTS 2
// Mindestrundenzeit in Millisekunden, alles darunter als Kontaktprellen interpretieren
#define MINROUNDTIME 500
// Pins zum Anschluss der Reedkontakte (D2, D3)
byte reedPins[]={2,3};
// Interrupt-Nummern zu diesen Pins
byte pinInterrupts[]={0,1};
// Interrupt-Behandlungsroutinen
void (*isrFunctions[])() = { timing1, timing2 };

// Start der letzten Runde in Millisekunden, volatile weil Zugriff auch aus ISR!
volatile long rundenAnfang[]={0,0};
// Rundenzeit der letzten Runde in Millisekunden
long rundenZeit[]={99990,99990};
// Rundenzeit der schnellsten Runde in Millisekunden
long rundenZeitRekord[]={99990,99990};
// Rundenzähler
int rundenAnzahl[]={0,0};


void setup() {
  // LCD Anzeige mit Spalten und Zeilen initialisieren
  lcd.begin(16, 2);
  // Anzeige LED-13 auf dem Board initialisieren
  pinMode(LEDPIN,OUTPUT);
  // Reedkontakte und ISR Routinen initialisieren
  for (int i=0;i<SLOTS;i++)
  {
    pinMode(reedPins[i],INPUT_PULLUP);
    attachInterrupt(pinInterrupts[i], isrFunctions[i], FALLING);
  }  
}

void timing1()
{
  if (millis()-rundenAnfang[0]>MINROUNDTIME)
    rundenAnfang[0]=millis();
}

void timing2()
{
  if (millis()-rundenAnfang[1]>MINROUNDTIME)
    rundenAnfang[1]=millis();
}

void RundenzeitenVerarbeiten()
{
  // Start der vorletzten Runde in Millisekunden
  static long rundenAnfangVerarbeitet[]={0,0};
  long curMillis;
  // Aktuellen Stand der millis() Funktion merken
  curMillis=millis();
  // Alle Slots auf Veränderungen der Rundenstartzeit abklappern
  for (int i=0;i<SLOTS;i++)
  { 
    if  (rundenAnfangVerarbeitet[i]!=rundenAnfang[i])
    { // Neue Runde seit dem letzten Loopdurchlauf
      rundenAnzahl[i]++; // Rundenzähler hochzählen
      // Rundenzeit ermitteln
      rundenZeit[i]=rundenAnfang[i]-rundenAnfangVerarbeitet[i];
      // Feststellen ob es eine schnellste Runde war
      if (rundenZeit[i]<rundenZeitRekord[i])
        rundenZeitRekord[i]=rundenZeit[i];
      // Am Ende diese Rundenzeit als verarbeitet merken  
      rundenAnfangVerarbeitet[i]=rundenAnfang[i];
    }
  }
}

void RundenanzahlAnzeigen()
{ static long lastUpdate;
  char lcdline[17];
  // Anzeige nur alle 500 ms aktualisieren
  if (millis()/500==lastUpdate) return;
  lastUpdate=millis()/500; 
  for (int i=0;i<SLOTS;i++)
  {
    snprintf(lcdline,sizeof(lcdline),"%d %5d Runden  ",i+1,rundenAnzahl[i]);
    lcd.setCursor(0,i);
    lcd.print(lcdline);
  }  
}

void RundenzeitenAnzeigen()
{ static long lastUpdate;
  long dieseRunde, dieserRekord;
  char lcdline[17];
  int zeit[4];
  // Anzeige nur alle 500 ms aktualisieren
  if (millis()/500==lastUpdate) return;
  lastUpdate=millis()/500; 
  for (int i=0;i<SLOTS;i++)
  {
    // Rundenzeit und schnellste Zeit auf Zeitangabe in hundertstel runden
    dieseRunde=(rundenZeit[i]+5)/10;
    dieserRekord=(rundenZeitRekord[i]+5)/10;
    zeit[0]=dieseRunde/100; // ganze Sekunden
    zeit[1]=dieseRunde%100; // hundertstel Sekunden
    zeit[2]=dieserRekord/100;  // ganze Sekunden
    zeit[3]=dieserRekord%100; // hundertstel Sekunden
    snprintf(lcdline,sizeof(lcdline),"%d%3d:%02d SR%3d:%02d",i+1,zeit[0],zeit[1],zeit[2],zeit[3]);
    lcd.setCursor(0,i);
    lcd.print(lcdline);
  }  
}

void loop()
{
  RundenzeitenVerarbeiten();
  if ((millis()/2000)%2==1)
    RundenzeitenAnzeigen();
  else 
    RundenanzahlAnzeigen();  
  // Nun noch die Pin-13 LED ein wenig blinken lassen, wenn man möchte  
  if (millis()-rundenAnfang[0]<200 || millis()-rundenAnfang[1]<200)
    digitalWrite(LEDPIN,HIGH);
  else  
    digitalWrite(LEDPIN,LOW);  
}

vielen vielen Dank jurs,

habe es gestern Abend noch umgesetzt und es funktionieret super...echt geil wie du das mit dem umschwenken auf die zweite Ebene gemacht hast wäre ich erstmal gar nicht drauf gekommen :wink:

Jedenfalls kommen mein Sohn und mein Enkel heute und wir werden viel Spass haben, kannst sicher sein das Dein Name fällt :wink:

Danke nochmal

jetzt habe ich noch einen Grund mehr mich mit der Programmierung zu beschäftigen....Es ist ein geiles Zeug