VM-Bordelektrik mit Arduino

wenn Du den Nano noch nicht programmiert hast, dann mach das bitte auch noch nicht.
Das schon, aber mit einem anderen Programm.
Da soll eine Zeichenkette direkt eingelesen werden.
Aber deine Version mit dem Puffer Zeichenweise auslesen ist wohl besser.
So war das auch im verlinkten Beispiel von Reinhard.

Leider weiss man ja nicht wodran es hapert , wenn s nicht funktioniert:
Ich drück derzeit an Uno ein Taste, die die onboard Led schaltet und gleichzeitig ein ‚H‘ and den Nano sendet.
Wenn der Nano ein ‚H‘ empfängt, soll dieser auch die onboard Led schalten.
Uno Led leuchtet bei Tastendruck, beim Nano passiert nix.
 
Ok!
Kann ich so auch nicht sagen...
Mein Nano ist wieder ok. Jetzt weiß ich auch, wie man den Bootloader neu installiert :)
Was ich eigentlich heute machen wollte: Testen, ob zwei Nanos an der seriellen funktionieren. Ich wollte mir den Pegel des RS232-Signals anschauen, ob der mit einem zweiten Nano signifikant einbricht.
Hole ich morgen nach. Ergebnis poste ich hier.

Grüße
Heiko
[DOUBLEPOST=1522956829][/DOUBLEPOST]
Blödsinn, es ist völlig egal was das Programm macht - unmittelbar nach dem Reset kommt immer(!) erst der Bootloader und wartet ne kleien Weile ob der Arduino programmiert werden soll. Erst dann wird das eigentliche Programm gestartet.
Und wenn man den Bootloader kaputt macht, dann kann man das Teil jederzeit über den ISP neu aufspielen.
Danke für das Blödsinn...

Die "kleine Weile warten" kann man aber kaum erkennen. Zudem läßt sich ja nicht feststellen, ob der Bootloader defekt ist oder ob man einfach in dieser kleinen Wartezeit nicht rankommt. Wenn's bei dir klappt, wunderbar. Ich habe genug Versuche damit durch heute.

Tschö
Heiko
 
Leider weiss man ja nicht wodran es hapert , wenn s nicht funktioniert
Das klingt nach "habe weder Oszi noch Logiktester". :unsure:
Wie weit kannst Du die Baudrate testhalber noch runterschrauben? Dass (irgend-)was auf der Leitung läuft, siehst Du bei niedriger Baudrate auch mit Hilfe einer an den Ausgang gehängten LED (mit Vorwiderstand), die in ihrer Helligkeit kurz einbricht. Besonders stark einbrechen tut sie, wenn das gesendete Zeichen viele Nullbits hat, z.B. Leerzeichen oder @.
Kannst Du auf dem nano feststellen, ob sich am UART überhaupt was tut? Evtl. ist die Baudrate falsch gesetzt (Taktteiler mit falscher Taktfrequenz berechnet o.ä.), so dass er Unsinn empfängt oder still und leise einen frame error feststellt.

Wenn andere Debugging-Möglichkeiten fehlen, kann man mit einer einzigen LED übrigens auch mehrere Bits nacheinander "ausblinken", indem man am Beginn eines Bits 200ms lang 20-30% PWM ausgibt und anschließend für 800ms je nach Bitwert ein- oder ausschaltet. Mehrere Bytes kann man durch verlängerte PWM-Phasen trennen. 1s pro Bit klingt recht lahm, aber 500ms finde ich zum Mitschreiben schon zu hektisch. Und einen kleinen Taster, mit dem man um Abbruch der Blinkerei bitten kann, kann man wahrscheinlich noch irgendwo anschließen.
 
Zuletzt bearbeitet:
Danke für das Blödsinn...
Bitte nicht zu ernst nehmen... War nett gemeint, aber Du kannst ja meinen Gesichtsausdruck beim Schreiben nicht sehen ;)

Die "kleine Weile warten" kann man aber kaum erkennen
Muss man ja auch gar nicht. Beim den Arduinos mit USB-schnittstelle (also auch Dein Nano) ist der RTS-pin der seriellen Schnittstelle über einen Kondensator + Pullup-widerstand mit Reset des µC verbunden. Programmiert man über die Arduino-IDE zieht diese RTS zu Beginn des Programmiervorgangs auf Masse und löst damit den Reset genau zum richtigen Zeitpunkt aus.
Bei einem Arduino ohne USB (z.B. ein Mini) - oder wenn man (ohne die Arduino-ide) zu Fuß programmiert - drückt und hält man einfach den Reset-knopf, startet den Programmiervorgang und lässt erst dann den Reset-knopf wieder los, der Bootloader meldet sich beim Programmierprogramm und das Programm wird aufgespielt.
So einfach kann die Welt sein :D
 
@Nobbi : Das mit dem Resetknopf habe ich genau so gemacht. Vielleicht war dann wirklich der Bootloader zerschossen.
 
aus unerfindlichen Gründen kaputten Bootloader hatte ich auch schon ein paar mal :(. Ließ sich durch Wiederaufspielen per ISP aber glücklicherweise immer wiederbeleben. Witzig ist ja, dass man wenn man mehr als einen Arduino besitzt dafür gar nichts groß für anschaffen muss sondern einfach einen Arduino als ISP für den zweiten benutzen kann.
 
Ich poste bald den aktuellen Stand: Umstellung auf "SoftwareSerial", damit die USB-Schnittstelle zum PC nicht doppelt genutzt wird. Das macht weniger Probleme.
Funktionieren tut es schon. Brauche nur noch etwas Zeit zum Aufbereiten und den nächsten Schritt möchte ich auch gleich mit einbauen.

Grüße
Heiko
 
So, weiter geht's. Hatte mich tatsächlich mit einem bug selbst reingelegt. Ein paar Minuten :censored::D hat es gedauert, bis ich auf meinen Fehler zwischen den Ohren gekommen bin.
Hier nun mein neuer Stand mit serieller Schnittstelle in Software realisiert. Erst einmal Bilder und darunter jeweils Text zur Erklärung:

Schaltungsaufbau:
Schaltung zu Stand A.png
Wie später auch im Code zu erkennen ist, wählte ich beim Uno (Zentrale, Sender) die Pins 11 und 12 für RX und TX. Beim Nano (Empfänger) sind es die Ports D3 (TX) und D2 (RX).

Code Uno (Zentrale):
Code:
#include <SoftwareSerial.h>

SoftwareSerial mySerial(11, 12); // RX, TX
int LED=13;  // Status-LED auf dem Board

void setup() {
  pinMode(LED, OUTPUT);
  mySerial.begin(9600);
}


// Alle Betriebsarten werden parallel in einem Byte
// uebertragen:
// Bit 0 (Wert 1) = Licht
// Bit 1 (wert 2) = Tagfahrlicht
// Bit 2 (Wert 4) = Bremslicht
// Bit 3 (Wert 8) = Blinker links
// Bit 4 (Wert 16) = Blinker rechts
// Bit 5 (Wert 32) = Hupe
// Bit 6 (Wert 64) = Innenraumbeleuchtung
// Bit 7 (Wert 128) = --Reserve--

void loop()  {

  // Licht
  digitalWrite(LED, HIGH);
  mySerial.write(byte(1));  // Licht an
  delay(10000);             // 10sec warten
  mySerial.write(byte(0));  // Licht aus
  digitalWrite(LED, LOW);
  delay(2000);

  // Tagfahrlicht
  digitalWrite(LED, HIGH);
  mySerial.write(byte(2));  // Tagfahrlicht an
  delay(10000);             // 10sec warten
  mySerial.write(byte(0));  // Tagfahrlicht aus
  digitalWrite(LED, LOW);
  delay(2000);

  // Bremslicht
  digitalWrite(LED, HIGH);
  mySerial.write(byte(4));  // Bremslicht an
  delay(10000);             // 10sec warten
  mySerial.write(byte(0));  // Bremslicht aus
  digitalWrite(LED, LOW);
  delay(2000);

  // Blinker links
  digitalWrite(LED, HIGH);
  mySerial.write(byte(8));  // Blinker links an
  delay(10000);             // 10sec warten
  mySerial.write(byte(0));  // Blinker links aus
  digitalWrite(LED, LOW);
  delay(2000);

  // Blinker rechts
  digitalWrite(LED, HIGH);
  mySerial.write(byte(16));  // Blinker rechts an
  delay(10000);              // 10sec warten
  mySerial.write(byte(0));   // Blinker rechts aus
  digitalWrite(LED, LOW);
  delay(2000);

  // Hupe
  digitalWrite(LED, HIGH);
  mySerial.write(byte(32));  // Hupe an
  delay(10000);              // 10sec warten
  mySerial.write(byte(0));   // Hupe aus
  digitalWrite(LED, LOW);
  delay(2000);

  // Innenraumbeleuchtung
  digitalWrite(LED, HIGH);
  mySerial.write(byte(64));  // Innenraumbeleuchtung an
  delay(10000);              // 10sec warten
  mySerial.write(byte(0));   // Innenraumbeleuchtung aus
  digitalWrite(LED, LOW);
  delay(2000);

  // Kombination
  // Tagfahrlich, Licht, Innenraumbeleuchtung
  digitalWrite(LED, HIGH);
  mySerial.write(byte(67));  // Kombination an
  delay(10000);              // 10sec warten
  mySerial.write(byte(0));   // Kombination aus
  digitalWrite(LED, LOW);
  delay(2000);
}

Code NANO (Empfänger):

Code:
#include <SoftwareSerial.h>

int LED=13;  // Status-LED
SoftwareSerial mySerial(2, 3); // Rx, Tx
char a;

void setup() {
  pinMode(LED, OUTPUT);
  mySerial.begin(9600); // initialize serial
  Serial.begin(9600);
  Serial.println("Arduino Nano Front");
}

// Alle Betriebsarten werden parallel in einem Byte
// uebertragen:
// Bit 0 = Licht
// Bit 1 = Tagfahrlicht
// Bit 2 = Bremslicht
// Bit 3 = Blinker links
// Bit 4 = Blinker rechts
// Bit 5 = Hupe
// Bit 6 = Innenraumbeleuchtung
// Bit 7 = --Reserve--

void loop() {
  if (mySerial.available()) {
    a = mySerial.read();
    Serial.write("---------- Start ----------\n");
    if (a & 1) {
      Serial.write("               Licht: ON\n");
    } else {
      Serial.write("               Licht: OFF\n");
    }
    if (a & 2) {
      Serial.write("        Tagfahrlicht: ON\n");
    } else {
      Serial.write("        Tagfahrlicht: OFF\n");
    }
    if (a & 4) {
      Serial.write("          Bremslicht: ON\n");
    } else {
      Serial.write("          Bremslicht: OFF\n");
    }
    if (a & 8) {
      Serial.write("       Blinker links: ON\n");
    } else {
      Serial.write("       Blinker links: OFF\n");
    }
    if (a & 16) {
      Serial.write("      Blinker rechts: ON\n");
    } else {
      Serial.write("      Blinker rechts: OFF\n");
    }
    if (a & 32) {
      Serial.write("                Hupe: ON\n");
    } else {
      Serial.write("                Hupe: OFF\n");
    }
    if (a & 64) {
      Serial.write("Innenraumbeleuchtung: ON\n");
    } else {
      Serial.write("Innenraumbeleuchtung: OFF\n");
    }
    Serial.write("---------- Ende ----------\n\n");
  }
}

Beschreibung:

Wie im Code ersichtlich, übertrage ich die von René angedachten Betriebszustände in einem einzigen Byte parallel. Es sind 7 Zustände, weshalb ein Bit sogar als Reserve frei bleibt. Der Code ist hoffentlich mit ausreichend Erklärungen und Ausgaben versehen. Wer dennoch Fragen hat, her damit!
Ich versuche das extra einfach zu halten, damit auch nicht-Programmierer folgen können.
Was passiert denn eigentlich?
Sender:
Es werden nacheinander alle Betriebszustände einzeln übertragen. Es folgt nach z.B. Licht "AN" eine 10-Sekunden-Pause. Dann wird alles ausgeschaltet und nach zwei weiteren Sekunden kommt der nächste Zustand.
Am Ende gibt es dann noch eine Kombination von drei eingeschalteten Zuständen.
Empfänger:
Relativ einfach. Kommt ein Byte über die Schnittstelle rein, wird mittels "Bit-Masken" der jeweilige Zustand ausmaskiert und erkannt. Es erfolgt eine Ausgabe aller erkannten Zustände. "Ausmaskieren" mittels Bits kann ich bei Bedarf gerne kurz erklären. Aber vielleicht versteht es auch jeder beim Lesen des Codes.
Die serielle-USB-Schnittstelle zum PC bleibt erhalten und dient als Ausgabemonitor.

Ausgabe:
Ausgabe zu Stand A.png

Viel Spaß beim Ausprobieren!

Grüße
Heiko
 
Ich hab da Null Ahnung, aber nur mal fürs Verständnis gefragt:
Wenn du jetzt die Bremse reinhauen würdest, würde das dann wahlweise (je nach Pausenlänge) erst nach 2 oder 10 Sekunden zB das Bremslicht aktivieren.
Oder habe ich da jetzt einen gravierenden Denkfehler nebst Knoten im Hirn ?
 
Ah, jetzt, ja, eine Insel. Ok, jetzt bin ich zumindest den Denkknoten los, Danke.
Wie ich schon schrub bin ich da eher unbeleckt von der Weisheit des Programmierens, allein das was ich meinte zu verstehen wollte nicht so recht logisch erscheinen.
Auf den ersten Blick dachte ich doch ernsthaft dies sei quasi das Betreiberprogramm für ein elektrisch komplett ausgestattetes Velpmobil.
 
Zum Verständnis: Wenn Du im aktuellen Demoprogramm eine einzelne Abfrage des Bremsschalters einbaust, würde er das nach spätestens 96 Sekunden merken. :D Und nach genau weiteren 96 Sekunden feststellen, dass er das Bremslicht wieder ausmachen kann... :sleep: (In der loop()-Funktion sind acht nacheinander abgearbeitete Blöcke, in denen jeweils 10 Sekunden geleuchtet und dann 2 Sekunden dunkel geschaltet wird, deswegen dauert ein Durchlauf 96 Sekunden.)

Praktisch würde man in dieser Schleife nur den Zustand der interessierenden Schalter abfragen, den neuen Zustand der ganzen Lichter bestimmen und ein neues Byte für die serielle Übertragung ausrechnen, möglichst ohne delay()-Aufrufe.
 
Praktisch würde man in dieser Schleife nur den Zustand der interessierenden Schalter abfragen, den neuen Zustand der ganzen Lichter bestimmen und ein neues Byte für die serielle Übertragung ausrechnen, möglichst ohne delay()-Aufrufe.
Jo. Das machen wir dann bald (y)

Grüße
Heiko
 
Nö. Blöde Fragen gibt es nicht :D

Warum verwendest Du nicht die dafür vorgesehenen TX und RX Pins?
Hatte ich oben bereits angedeutet. Es macht zuviele Probleme, denn:
All Arduino boards have at least one serial port (also known as a UART or USART): Serial. It communicates on digital pins 0 (RX) and 1 (TX) as well as with the computer via USB. Thus, if you use these functions, you cannot also use pins 0 and 1 for digital input or output. Quelle: Arduino Reference
Wenn es doof läuft (wie bei mir), dann kann man nach dem Aufspielen den Arduino per USB nicht mehr erreichen. Deshalb der Weg über eine Software-Lösung für die serielle Schnittstelle. Am Ende, wenn alles läuft, könnte man durchaus wieder auf die dafür vorgesehenen PINs wechseln. Da spricht nichts dagegen. Für die Entwicklungszeit ist es aber so angenehmer.

Grüße
Heiko
 
So, ich möchte nicht zuviele Schritte auf einmal machen. Deshalb kommt hier zunächst mein Entwurf zur Programmstruktur der Zentrale (UNO):

Code:
#include <SoftwareSerial.h>

SoftwareSerial mySerial(11, 12); // RX, TX
int LED = 13;                    // Status-LED auf dem Board
byte sendeByte = 0;              // das Byte, welches an die Front und ans Heck gesendet wird

void setup() {
  pinMode(LED, OUTPUT);
  mySerial.begin(9600);
}


// Alle Betriebsarten werden parallel in einem Byte
// uebertragen:
// Bit 0 (Wert 1) = Licht
// Bit 1 (wert 2) = Tagfahrlicht
// Bit 2 (Wert 4) = Bremslicht
// Bit 3 (Wert 8) = Blinker links
// Bit 4 (Wert 16) = Blinker rechts
// Bit 5 (Wert 32) = Hupe
// Bit 6 (Wert 64) = Innenraumbeleuchtung
// Bit 7 (Wert 128) = --Reserve--

void loop()  {
 
  sendeByte = 0;  // das zu sendende Byte zu Beginn loeschen
 
  // Lichtschalter abfragen
  if (zustandLichtschalter())
  {
    sendeByte = 1;
  }
 
  // Tagfahrlichtschalter abfragen
  if (zustandTagfahrlicht())
  {
    sendeByte += 2;
  }
 
  // Bremslichttaster abfragen
  if (zustandBremslicht())
  {
    sendeByte += 4;
  }
 
  // Blinkerschalter links abfragen
  if (zustandBlinkerLi())
  {
    sendeByte += 8;
  }
 
  // Blinkerschalter rechts abfragen
  if (zustandBlinkerRe())
  {
    sendeByte += 16;
  }

  // Hupenschalter abfragen
  if (zustandHupe())
  {
    sendeByte += 32;
  }

  // Innenraumbeleuchtungsschalter abfragen
  if (zustandInnenraumbel())
  {
    sendeByte += 64;
  }
 
  // Byte senden
  mySerial.write(sendeByte);
  delay(5000);
}

bool zustandLichtschalter()
{
  return false;
}

bool zustandTagfahrlicht()
{
  return true;
}

bool zustandBremslicht()
{
  return false;
}

bool zustandBlinkerLi()
{
  return false;
}

bool zustandBlinkerRe()
{
  return false;
}

bool zustandHupe()
{
  return false;
}

bool zustandInnenraumbel()
{
  return true;
}
Der Code sollte selbsterklärend sein, denke ich. Es werden alle Schalter/Taster durch Aufrufen ihrer jeweiligen "Abfragefunktion" auf ihren Zustand überprüft. Das zu sendende Byte wird nacheinander ergänzt - je nach Zustand des gerade abgefragten Schalters.
Die Summe aller Schalter-Zustände wird am Ende gesendet, es kommt eine 5sek-Pause und dann geht es wieder von vorn los. Es fehlen nun noch die sinnvollen Inhalte für die ganzen Abfragefunktionen. Dazu gehört sicher auch ein Entprellen von Schaltern/Tastern. Und schlußendlich sollte natürlich die 5sek-Pause entfernt werden. Dann rennt der UNO so schnell er kann durch die Abfragen. Mal schauen, was da am Ende erreicht wird.
Ich weiß im Moment auch gar nicht, ob die serielle Schnittstelle mit derzeit 9600bps den UNO ausbremsen würde... aber das alles später.

Der NANO kann zum vorigen Programm unverändert bleiben, da dieser momentan nur eine Ausgabe des empfangenen Bytes auf den seriellen Monitor macht.
Die Ausgabe sollte (alle 5sek) so aussehen:
Code:
---------- Start ----------
               Licht: OFF
        Tagfahrlicht: ON
          Bremslicht: OFF
       Blinker links: OFF
      Blinker rechts: OFF
                Hupe: OFF
Innenraumbeleuchtung: ON
---------- Ende ----------

Grüße
Heiko
 
@Heiko
Blöde Frage. Warum verwendest Du nicht die dafür vorgesehenen TX und RX Pins?
Das Problem ist: wenn du die Serielle Schnittstelle verwendest, dann kannst du über den Seriellen Monitor in der IDE speziell am Empfänger nichts auswerten/überwachen.
Manchmal Klappts aber oft hakt es...
Ich habe im Gegensatz zu Heiko ein LCD-Display über I2C an den Empfänger zum Überwachen angeschlossen.
Heiko ist aber deutlich schneller, weil er vermutlich mehr Zeit reinsteckt als ich.
Beim Kommunikationsprotokoll würde ich aber verschwenderischer sein. Vermutlich 4Byte für einen Befehl statt ein Byte für alles.
 
Es fehlen nun noch die sinnvollen Inhalte für die ganzen Abfragefunktionen. Dazu gehört sicher auch ein Entprellen von Schaltern/Tastern.
Für einen einzelnen Schalter könnte sowas hier taugen:
Code:
unsigned char ENTPRELLDAUER = 30; // Millisekunden
int PIN_NR_LICHT = ???;  // passende Pin-Nr. aussuchen

setup() {
  // ...
  // nicht die Funktion ersetzen, sondern nur das Folgende zu den
  // bereits vorhandenen Initialisierungen hinzufügen:
  pinMode (PIN_NR_LICHT, INPUT_PULLUP);
  digitalWrite (PIN_NR_LICHT, HIGH);
  // ...
}

bool zustandLichtschalter (void) {
  static bool oldState = false, debouncing = false;
  static unsigned char oldTVal; // Initialisierung unnötig, und 255ms sollten reichen
  bool curState;
  unsigned char curTVal;

  // Nach einem erkannten Zustandswechsel wird zunächst die Entprelldauer
  // abgewartet.  Anschließend(!!!) wird der dann aktuelle Zustand als neuer
  // Pin-Zustand übernommen und ausgegeben.  Weitere Zustandswechsel
  // innerhalb der Entprelldauer werden ignoriert, sie führen auch nicht
  // dazu, dass die Entprelldauer verlängert wird.  Eher für Schalter,
  // die dauerhaft HIGH oder LOW sind.
  //
  // Alternative für Tasten, die nur kurz gedrückt werden:
  // Den neuen Pin-Zustand gleich beim erkannten Zustandswechsel übernehmen.
  // Dann reagiert das Programm etwas eher und „übersieht“ nicht so leicht kurze
  // Tastenbetätigungen, aber kurze Störungen führen dazu, dass über die
  // gesamte Entprelldauer ein falscher Zustand ausgegeben wird.

  if (debouncing) {
    curTVal = (unsigned char)millis();
    if (curTVal - oldTVal > ENTPRELLDAUER) {
      debouncing = false;
      // Für Tasten: oldState-Zuweisung hier auskommentieren:
      oldState = (digitalRead (PIN_NR_LICHT) == HIGH ? true : false);
    };
  } else {
    curState = (digitalRead (PIN_NR_LICHT) == HIGH ? true : false);
    if (curState != oldState) {
      oldTVal = (unsigned char)millis();
      debouncing = true;
      // Für Tasten: oldstate hier setzen:
      // oldState = curState;
    };
  };

  return oldState;
}

Für die Blinker muss die Bestimmung der Schalterzustände wahrscheinlich von den Funktionen zustandBlinkerLi() und zustandBlinkerRe() getrennt werden.
 
Das Prellen ist aber nur ein Problem wenn der Taster einen Schalter ersetzt. Bei mir ist Blinker an einmal drücken und Blinker aus nochmal drücken. Dann ist das Prellen ein Problem und muss per Software (siehe oben) unterdrückt werden. Bei der Hupe ist es kein Problem, da ist der Taster nur ein Taster. Drücken macht die Hupe an, loslassen macht sie aus. Dabei ist es Wurst wenn die Software das Prellen an den Ausgang weitergibt. Das hört man ja nicht. Wenn man echte Schalter verwendet ist es auch kein Problem.
 
Zurück
Oben Unten