Kinderauto mit Nano Every und wenig Erfahrungen

Wo soll ich anfangen?

Erstmal damit:

Hi ihr hier, von mir aus. Mein erster Post, bitte seid gnädig.

Ich bin ein Kinderauto für meine Enkeling und zukünftigen Enkel am bauen.
Und würde euch bitten, mal über meinen Arduino Code zu schauen um Anfängerfehler zu erkennen und mir mitzuteilen.

Kurzer Überblick:

  • 2 Bleigelbatterien in Reihe für 24V
  • 2 Motoren DC die über den Controller unten geregelt werden sollen
  • Gaspedal mit Hallsensor
  • Original Vor- / Rückwärtsschalter wird übernommen aber über Relais an den Controller gegeben
  • RC Fernbedienung und Empfänger aus dem Modellbau

Dazu verwende ich:

-Arduino Nano Every

-Klemmboard dafür

AZDelivery Nano Terminal Adapter Board mit Schraubklemmen

-Relaismodul

Elegoo 8 Kanal DC 5V Relaismodul mit Optokoppler für Arduino UNO R3 1280 DSP ARM PIC AVR STM32 Raspberry Pi

-Motorcontroller

Akozon DC Motor Drehzahlregler PWM Steuerschalter Regler 10-50V 100A 5000W Hochleistungs Motor Drehzahlregler Schalter

-Gaspedal

elektrische gaspedal für Kart Gas elektrische Auto - Kinder - Dreirad mit fußpedal elektrische geschwindigkeitskontrolle

-Netzteile für die Spannungen

4 Stücke Einstellbarer LM2596S DC-DC Abwärtswandler Abwärt Spannungsregler Leistungsmodul 36V 24V 12V bis 5V 2A Spannung Stabilisator mit Digitaler Voltmeter Anzeige

-Ein klein bisschen angeeignetes Wissen

Arduino: Kompendium: Elektronik, Programmierung und Projekte

-Fernsteuerung und Empfäner diesen Typs

-Hat nichts mit Arduino zu tun, aber diesen Fahrtenregler für die Lenkung

Leider konnte ich nicht alle Links mit Posten, aber alles mit #ist von Amazon

Nun zu meinem Code:

//mittlerwert lib
#include <RunningMedian.h>
RunningMedian samples = RunningMedian(8);  //anzahl durchgänge für mittelwer



//gaspedal
int GPdrossel = 125;  //drossel gaspedal standart 125
int nullgas = 55;     //nullpunkt gaspedal
float gpteiler = 0.6; //gpteiler
int gaspedal = A1;    //Gaspedal Input
int vargaspedal = 0;  //variable gaspedal
int GPschub = 0;      //Schub von Gaspedal

//knüppel

int kn1 = 0;          //eingang knüppel pin 4
int kn2 = 0;          //eingang knüppel pin 6
bool KNvor = 0;       //knüppel vorwärts
bool KNzur = 0;       //knüppel rückwärts
int nitroknopf = 0;   //nitro eingang pin 2
bool nitro = 0;       //nitro Knopfvariable


// fernbedienung
int CH_2 = 3;         //rc empfänger pin pin 3
int FBin = 0;         //fernbedienung live input
int fb = 0;           //rc empfänger pulsezeit enstörrt & korrigiert
int nullvor = 1260;   //nullpunkt vorwärts mittelwert etwa 1230
int nullzur = 1190;   //nullpunkt rückwärts
int fbstoerL = 900;   //störungskorrektur unten 900
int fbstoerH = 1900;  //störungskorrektur oben 1900
int FBschub = 0;      //gasstellung zum poti
bool FBvor = 0;       //vorwärts von FB
bool FBzur = 0;       //rückwärts von FB


// ausgabe zum controller
int Cschub = 0;       //variable ausgang zum poti
bool Cvor = 0;        //vorwärtsgang
bool Czur = 0;        //rückwärtsgang



// ------------------------------------------------------------------------------------------------------------------------------------------------------------


void setup() {

  Serial.begin(19200); //usb schnitstelle monitor

//knüppel initialisieren
  pinMode(4, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);

//nitro initialisieren
  pinMode(2, INPUT_PULLUP);

//relais initialisieren
  pinMode(9, OUTPUT);
  pinMode(10, OUTPUT);

//Controller initialisieren
  pinMode(7, OUTPUT);

}

//-----------------------------------------------------------------------------------------------------------------------------------


void loop() {
//reset
Cvor = 0;
Czur = 0;
Cschub = 0;
FBvor = 0;
FBzur = 0;
GPschub = 0;
FBschub = 0;
KNvor = 0;
KNzur = 0;
nitro = 0;
nitroknopf = 0;

//einlesen eingänge

analogRead(gaspedal);
digitalRead(2);   //nitro knopf
digitalRead(4);   //KNvor
digitalRead(6);   //KNzur



//gaspedal
  vargaspedal = analogRead(gaspedal)/4-nullgas; //korrigiertes gaspedal
  
  //nullkorrektur gaspedal
  if (vargaspedal<0) {
    vargaspedal=0;
  }

  GPschub = vargaspedal/gpteiler; //vargaspedal input / gpteiler = GPschub

  //begrenzer gaspedal
  if (GPschub > 255) {
    GPschub =255;
  }

  //drossel & nitro gaspedal
  
  nitroknopf = digitalRead(2);
  
  if(nitroknopf == LOW) {    //wenn nitro gedrückt, keine drossel
    nitro = 1;
  }

  if (nitro<1) {             //wenn nitro nicht gedrückt, GPschub auf auf GP drossel begrenzt
    if (GPschub>GPdrossel) {
      GPschub = GPdrossel;
    }
  }






//knüppel
  KNvor = 0;
  KNzur = 0;
  kn1 = digitalRead(4);
  kn2 = digitalRead(6);
  
  if (kn1 == LOW) {
    KNvor = 1;
  }

  if (kn2 == LOW) {
    KNzur = 1;
  }





  
//fernbedienung
  
  //signal auslesen
  int FBin = pulseIn(CH_2, HIGH, 25000); //dekodierung empfängerpin  in pulsezeit
  fb = FBin;  	                         //pulzeit von empfänger zu variable FB

  //FB Entstörung
  if (FBin<fbstoerL) {                   //wenn das Signal von FBin außerhalb der Tolleranz fbstoerL oder fbstoerH ist, wird fb auf nullpunkt (1230) gesetzt
    FBvor = 0;
    FBzur = 0;
    FBschub = 0;
    fb = 1230;
  }


  if (FBin>fbstoerH) {
    FBvor = 0;
    FBzur = 0;
    FBschub = 0;
    fb = 1230;
  }




  //mittelwertbestimmung mit RunningMedian.lib um weitere störungen zu filtern
  int x = fb;                         //entsörtes signal wird zur mittelwertbestimmung übergeben
  
  samples.add(x);

  long m = samples.getMedian();

  fb = m;                             //mittelwert wird als fb zurück gegeben






  //erkennung richtung und schub

  //vorwärts
  if (fb >nullvor) {                  //entstörtes und gemitteltes Signal wird mit den Nullpunkt Variablen verglichen/umgerechnet, vor- oder rückwärtsgang eingelegt und FBschub geschrieben sowie auf max 255 begrenzt
    FBvor =1;
    FBschub = fb-nullvor;
    FBschub = FBschub/2;
  }
  //rückwärts
  if (fb <nullzur) {
    FBzur =1;
    FBschub = nullzur-fb;
  }


  //schubbegrenzung
  if (FBschub>255) {
    FBschub =255;
  }


//MOTORCONTROLLER

  //C-vorwärts
  if (FBvor>0) {       //wenn vorwärtssignal von Fernbedienung kommt, wird alle anderen Variablen überschrieben und vorwärtsgang eingelegt
    KNvor = 0;
    KNzur = 0;
    Czur = 0;
    Cvor = 1;  
  }
  else {
    Cvor = KNvor;     //kein signal von Fernbedienung, Knüppelstellung wird übernommen
  }

  //C-rückwärts
  if (FBzur>0) {       //wenn rückwärtssignal von Fernbedienung kommt, wird alle anderen Variablen überschrieben und rückwärtsgang eingelegt
    KNzur = 0;
    KNvor = 0;
    Cvor = 0; 
    Czur = 1; 
  }
  else {
    Czur = KNzur;     //kein signal von Fernbedienung, Knüppelstellung wird übernommen
  }

  //C-schub
  if (FBschub>5) {    //wenn an der Fernbedienung merkbar Gas gegeben wird, wird Gaspedal auf 0 gesetzt und FBschub auf Controllerschub geschrieben
    Cschub = FBschub;
    GPschub = 0;
  }
  else {
    Cschub = GPschub;//kein Schubsignal von der Fernbedienung, Controllerschub wird vom gedrosseltem Gaspedal übernommen
  }


//Relaiskarte
  
  //leerlauf
  if (Cvor+Czur == 0) {
    digitalWrite(9, HIGH);
    digitalWrite(10, HIGH); 
  }
  
  //Cvorwärts
  if (Cvor == 1) {
    digitalWrite(9, HIGH);
    delay(80);
    digitalWrite(10, LOW); 
  }

  //Crückwärts
    if (Czur == 1) {
    digitalWrite(10, HIGH);
    delay(80);
    digitalWrite(9, LOW); 
  }

analogWrite(5, Cschub);



//monitor ausgaben

  Serial.print("  GP: ");Serial.print(vargaspedal);
  Serial.print("  GPschub: ");Serial.print(GPschub);
  Serial.print("  KNvor: ");Serial.print(KNvor);
  Serial.print("  KNzur: ");Serial.print(KNzur);
    Serial.print("\t");Serial.print("\t");
  Serial.print("  FBin: ");Serial.print(FBin);
  Serial.print("  FB: ");Serial.print(fb); 
  Serial.print("  FBschub: ");Serial.print(FBschub);
  Serial.print("  FBvor: ");Serial.print(FBvor);
  Serial.print("  FBzur: ");Serial.print(FBzur);
    Serial.print("\t");Serial.print("\t");
  Serial.print("AUSGABEN:");
  Serial.print("\t");
  Serial.print("  Cvor: ");Serial.print(Cvor);
  Serial.print("  Czur: ");Serial.print(Czur);
  Serial.print("  Cschub: ");Serial.print(Cschub);



 //nicht löschen!!
  Serial.println(" ");
  digitalWrite(7, LOW);
}


Soweit so gut.

Ich hoffe es ist alles halbwegs aktzeptabel und verständlich kommentiert und geschrieben.

Bitte teilt mir eure gedanken und anregungen mit.

Wie gesagt, ich bin da NOOB, das was ich gemacht habe funktioniert ganz gut, aber bevor ich alles zusammenbaue, würde ich gern die PRO- meinungen hören.

Für Anmerkungen und Kritik bin ich hier....

Grüße

AMORph01

BTW, zur Zeit sieht es so aus:

Alles noch im Bastelmodus

Hallo,

ich möchte dich nicht gleich überfordern, aber paar kleine Dinge sollten bei einem Spielzeugauto schon beachtet werden. Auch wenn es erstmal funktioniert. Kann bei anderer Verwendung wie du es derzeit nicht machst ggf. anders reagieren. Deswegen fasse zusammengehörende Dinge in ein struct zusammen. Werfe nichts zusammen was nicht zusammengehört. Mach alles konstant was sich nicht ändert bzw. sich nicht verändern darf. Das bewahrt einem vor groben Unfug und schwer findbaren Fehlern, die Logikfehler sind und der Compiler nicht sieht und damit auch nicht warnen kann. Schalte in der IDE alle Fehlerausgaben ein. Entferne alle unbenutzten Variablen. bspw. "standard", "nullpunkt"?

Bsp. Gaspedal

struct GasPedal
{
  const int standard = 125;     // drossel standard 125
  const int nullpunkt = 55;     // 
  const float gpteiler = 0.6;   //
  const byte pin = A1;          // 
  int stellung = 0;             // aktueller Wert
};
GasPedal gaspedal;

Zugriff erfolgt dann mittels Punkt-Operator auf die Elementvariable (pin) des Objekts (gaspedal).

gaspedal.pin
gaspedal.stellung

Damit hätte man schon einmal eine klare Trennung. Was ist GPSchub? Hat das was mit der Motoransteuerung zu tun? Wenn ja gehört das nicht zum Gaspedal. Betrachte alle Hardware für sich und beschreibe deren Eigenschaften die du davon brauchst. Macht auch die Namensfindung etwas leichter. Vergebe auch für alle Pins sprechende Namen. Definiere sie konstant global oder im struct.

Sowas hier vermeide auch. Neue Wertzuweisungen erst durchführen wenn es wirklich an die Reihe kommt. Ich wüßte jetzt nicht warum man das vorher machen sollte. Falls durch das Programm gesprungen wird und die Variable nicht neu gesetzt aber benötigt wird, hat man plötzlich 0 statt den alten Wert oder so ähnlich.

void loop() {
//reset
Cvor = 0;
Czur = 0;
Cschub = 0;
FBvor = 0;
FBzur = 0;
GPschub = 0;
FBschub = 0;
KNvor = 0;
KNzur = 0;
nitro = 0;
nitroknopf = 0;

Sowas ist auch bestimmt fehlerträchtig. Benötigst du irgendwann x der m nochmal?

//mittelwertbestimmung mit RunningMedian.lib um weitere störungen zu filtern
  int x = fb;                         //entsörtes signal wird zur mittelwertbestimmung übergeben
  samples.add(x);
  long m = 0;//samples.getMedian();
  fb = m;                             //mittelwert wird als fb zurück gegeben

Wenn nein, dann lieber gleich so.

samples.add(fb);
fb = samples.getMedian();

Ob das so günstig ist gleich den alten Wert zu überschreiben weiß ich aktuell nicht genau.

Weil das hier ist auch doppelt gemoppelt.

int FBin = pulseIn(CH_2, HIGH, 25000); //dekodierung empfängerpin  in pulsezeit
fb = FBin;                             //pulzeit von empfänger zu variable FB

Dann könnte man auch gleich schreiben
fb = pulseIn(CH_2, HIGH, 25000);

Ich würde wohl eher unterteilen in aktuellen Einlesewert und dem aktuellen median der dann überall verwendet wird.

struct Fernbedienung
{
  int aktuell = 0;
  int median = 0;
  ... ggf. noch mehr		
};

Und dann sollte man sich klare abgetrennte Funktionen schreiben. Die nur das machen was der Funktionsname sagt. Dazu gehört etwas Disziplin. Am Anfang schreibt wohl jeder rein was ihm gerade einfällt und merkt dann später das es besser ist lieber ein zwei Funktionen mehr zu haben die dafür aufgeräumter sind.

Bsp. Fernbedienung

int readFernbedienung (const byte pin, const int limitLow, const int limitHigh)
{
  // Pulszeit vom Empfänger lesen und Limits prüfen
  int value = pulseIn(CH_2, HIGH, 25000); //dekodierung empfängerpin  in pulsezeit
  
  // FB Entstörung
  if ( (value < LimitLow) || (LimitHigh < value) ) {  // wenn das Signal von FBin außerhalb der Tolleranz fbstoerL oder fbstoerH ist, wird fb auf nullpunkt (1230) gesetzt
    FBvor = 0;
    FBzur = 0;
    FBschub = 0;
    value = 1230;
  }
  return value;
}

Bsp. Median

int mittelwertbildung (const int value)
{
  samples.add(value);
  return samples.getMedian();
}

Die Funktion rufst du in loop auf. Jetzt kommt es darauf ob du Verschachtelungen magst oder erstmal die Lesbarkeit im Vordergrund steht. Für mich immer erstmal die Lesbarkeit.

const int tempFB = readFernbedienung (pinFB, fbstoerL, fbstoerH);
const int tempMedian = mittelwertbildung (tempFB);

Ob das auf 0 setzen der vielen Variablen bei Limitverletzung gemacht werden muss weiß ich nicht. Das kannst nur du wissen. Hinterfrage es jedoch immer wieder. Hinterfrage auch immer den verwendeten Datentyp aller Variablen. Das habe ich nicht geprüft. Passt in allen möglichen Fällen unter Annahme der Extreme das Ergebnis rein oder kommt es zu Wertebereichsüberläufen ... ?

Das sind so die Vorschläge was man aufs Erste ändern könnte. Damit ist der Code aufgeräumter, wenn aufgeräumter besser lesbar, wenn besser lesbar verringern sich Fehler, weniger Fehler macht alles sicherer.

Vor Fahrzeugübergabe alles ausreichend testen. :rofl:

1 Like

Hi,

zuerstmal danke, das du dir die Zeit genommen hast, eine tolle Kritik zu schreiben.
Das hat mich sehr gefreut und werde es umsetzten.
Zu deinen Fragen möchte ich aber jetzt schon Stellung nehmen.

Was ist GPSchub? Hat das was mit der Motoransteuerung zu tun? Wenn ja gehört das nicht zum Gaspedal.

Die Schub Variablen sind der eigendliche Grund für den einbau eines Arduino, nämlich um Gaspedal (ausgangsbereich 0,8 bis 4,7V), Fernbedienungssignal (Pulsslänge 900 bis 1900ms) zu vereinheitlichen sowie zu Verarbeiten (Stichwort Drossel/Nitro) , danach soll ein Signal (0 bis 5V) an den Motorcontroller gehen.

Sowas hier vermeide auch. Neue Wertzuweisungen erst durchführen wenn es wirklich an die Reihe kommt. Ich wüßte jetzt nicht warum man das vorher machen sollte. Falls durch das Programm gesprungen wird und die Variable nicht neu gesetzt aber benötigt wird, hat man plötzlich 0 statt den alten Wert oder so ähnlich.

void loop() {
//reset
Cvor = 0;
Czur = 0;
Cschub = 0;
FBvor = 0;
FBzur = 0;
GPschub = 0;
FBschub = 0;
KNvor = 0;
KNzur = 0;
nitro = 0;
nitroknopf = 0;

Der Gedankengang dahinter, ist das bei jedem neuen Programmdurchlauf alle kritischen Werte zurückgesetzt und neu errechnet werden. Am Ende des Programmdurchlaufs sollen dann alle neu errechneten, aktuellen Werte an den Motorcontroller gehen.

Sowas ist auch bestimmt fehlerträchtig. Benötigst du irgendwann x der m nochmal?

//mittelwertbestimmung mit RunningMedian.lib um weitere störungen zu filtern
  int x = fb;                         //entsörtes signal wird zur mittelwertbestimmung übergeben
  samples.add(x);
  long m = 0;//samples.getMedian();
  fb = m;                             //mittelwert wird als fb zurück gegeben

Wenn nein, dann lieber gleich so.

samples.add(fb);
fb = samples.getMedian();

Das hat mir besonderst gefallen, natürlich brauche ich x nicht nochmal, danke dafür :slight_smile:

Weil das hier ist auch doppelt gemoppelt.

int FBin = pulseIn(CH_2, HIGH, 25000); //dekodierung empfängerpin  in pulsezeit
fb = FBin;                             //pulzeit von empfänger zu variable FB

Dann könnte man auch gleich schreiben

fb = pulseIn(CH_2, HIGH, 25000);

Nunja, da gebe ich dir erst mal recht, muss aber sagen, dass das noch ein überbleibsel aus der entstörung der Fernbedienung ist.

Wir betrachten dazu kurz die Ausgaben der Seriellen Schnittstelle bei einer provozierten extremen Funkstörung:
(Fernbedienung ist aus, sowie ein Störsender, in dem Fall intressanterweise mein Monitor, direkt daneben)

Ich habe damit, das Live Signal mit dem Mittelwert verglichen, damit ich ein gefühl dafür bekomme, wie hoch ich

RunningMedian samples = RunningMedian(8); //anzahl durchgänge für mittelwert

setzen kann, ohne die Fernbedienung zu träge zu machen, gleichzeit aber möglichst Störungsfrei zu bleiben.

Ob das auf 0 setzen der vielen Variablen bei Limitverletzung gemacht werden muss weiß ich nicht. Das kannst nur du wissen.

Naja, da dachte ich "Safty first!" immerhin sitzt dann später mal ein Kind drinne.
Ich hielt und halte es für besser, wenn etwas nicht stimmt bleibt es stehen, sowie wenn ich über die Fernbedienung eingreifen muss, werden alle Befehle vom Kind überschrieben und ausgeschaltet.

Das sind so die Vorschläge was man aufs Erste ändern könnte. Damit ist der Code aufgeräumter, wenn aufgeräumter besser lesbar, wenn besser lesbar verringern sich Fehler, weniger Fehler macht alles sicherer.

Ich werde das natürlich nach und nach umsetzten und danke nochmal für deine Zeit.

Vor Fahrzeugübergabe alles ausreichend testen. :rofl:

Natürlich, muss auch noch einen Helm besorgen und denn Sicherheitsgurt einbauen.

Hallo,

um Missverständnisse vorzubeugen. Ich konnte mir schon vorstellen das eine Variable wie GPschub irgendwie benötigt wird. Das würde ich jedoch dem Motor verordnen. Dazu für den Motor ebenfalls ein struct anlegen.

Bspw.

struct Motor
{
  const byte pin =
  int leistung = 0;   // GPschub Ersatz
};
Motor motor;

Im Programm wird dann irgendwo passend der berechnete Leistungswert an den Motor (motor.leistung) übergeben. Damit wird dann der Motorcontroller angesteuert.

Du merkst ich würde Bestandteile des Autos in einzelne Baugruppen zerlegen und dessen Eigenschaften definieren. Sprich im wahrsten Sinne Struktur reinbringen. :wink: Die Baugruppen reden an bestimmten Stellen im Programm miteinander, sprich tauschen Daten aus. Ansonsten bleiben sie unter sich. Kann man später immer noch immer weiter vertiefen. Falls du das mit dem struct anlegen, anwenden und Funktionen schreiben durchziehen kannst, wäre dir schon sehr geholfen.

Wegen den "kritischen Werten". Was soll sich daran ändern wenn nichts neu zugewiesen wird? Vor einer Zuweisung müssen ggf. Limit beachtet werden, danach ist es sowieso zu spät. Wenn es kritisch ist, dann so wie du deine Fernbedienungswerte prüftst. Ich halte es für kritisch ohne Grund Werte zu nullen. Wenn es einen ganz konkreten Grund gibt okay, dann den Grund kommentieren. Nur zum Spass nullen eher nein. Globale Variablen behalten ihren Wert bis zur neuen Zuweisung eines solchen. Da passiert nichts kritisches. Es wäre aber fatal wenn aus irgendeinem Grund die Motorleistung zwischen berechneten Wert und 0 schwanken könnte. So weit mein Gedanke.

1 Like

Ja.
Vor allem dürfte ein Wert nach einer Fehler-0 (= Notaus ?) nicht von selbst wieder loslegen.
Das wäre zumindest kritisch bzw. jeweils einzeln das Fehlerverhalten zu prüfen.

1 Like

Das mit dem strukt muss ich mir erst mal anschauen, kenne es noch nicht.

Aber da es mir deutlich geraten wurde, werde ich es wohl umsetzen.

Auch das mit dem null setzen der variablen werde ich nochmal überarbeiten.

Danke für das deutliche Feedback.

Nun werde ich aber zuerst den tag mit meiner liebsten im botanischen garten bonn verbringen :slight_smile:

Hallo nochmal,

ich hoffe ich habe alle kritik beherzigt, verstanden und umgesetzt.

intressanterweise hat es echt spaß gemacht alles in structs zu gruppieren.
Ihr hattet absolut recht, das steigert die Übersicht ungemein.
Hochachtung davor :wink:

Witzig war es, was das sketch gemacht hat, nachdem ich den globalen reset entfernt habe (es hat alles durcheinander gewürfelt). Der tip mit den Variablen zu spielen, nur dann wenn sie gebraucht oder verändert werden war echt Gold wert.

Was ich auch echt toll fande, wärend ich am umschreiben war, ist der fakt das durch struct ein paar variablen entfernbar geworden sind.

Ich hoffe ihr habt nochmal Zeit und Muse dazu, die überarbeitete Version von dem Code zu lesen, bzw mir gerne weitere Kritik und Anregungen mitzuteilen.

Insbesondere wäre ich dankbar über hinweise, wo und wie ich notaus/nothalt funktionen implementieren könnte.

//mittlerwert lib
#include <RunningMedian.h>
RunningMedian samples = RunningMedian(8);  //anzahl durchgänge für mittelwert



//gaspedal
struct GasPedal
{
const int drossel = 125;      //drossel gaspedal standart 125
const int nullpunkt = 55;     //nullpunkt gaspedal (vom Hallsensor wird 0,8V bzw 55 in nullstellung ausgegeben), wird zur berechnung des schubs 0-255 gebraucht
const float teiler = 0.6;     //gpteiler (gaspedal gibt 0 bis 156 aus, durch den teiler wird etwa 0 bis 260 ausgegeben)
const byte pin = A1;          //Gaspedal Inputpin
int vargaspedal = 0;          //variable gaspedal
int schub = 0;                //Schub von Gaspedal
};
GasPedal gaspedal;




//Karosse
struct Karosserie
{
const byte pinvor = 4;      //Pin vorwärtsgang
const byte pinzur = 6;      //Pin rückwärstgang
const byte pinnitro =2;     //Pin Nitroknopf
bool KNvor = 0;             //knüppel vorwärts
bool KNzur = 0;             //knüppel rückwärts
};
Karosserie karosserie;

// fernbedienung
struct Fernbedienung
{
const byte pin = 3;         //rc empfänger pin(Channel2 am empfänger) = pin 3 am arduino
const int nullvor = 1260;   //nullpunkt vorwärts (mittelwert, dh an der fernbedienung ist der knüppel in ruhestellung, etwa 1230)
const int nullzur = 1190;   //nullpunkt rückwärts (wird der knüppel über 1260 oder unter 1190 bewegt, registriert das program einen steuerbefehl)
const int stoerL = 900;     //störungswert unten 900 (pulsezeiten unter 900 oder über 1900 sind Funkstörungen)
const int stoerH = 1900;    //störungswert oben 1900
int in = 0;                 //fernbedienung live input
int pulsezeit = 0;          //rc empfänger pulsezeit enstörrt & korrigiert
int schub = 0;              //gasstellung zum motor
bool vor = 0;               //vorwärtsgang von FB
bool zur = 0;               //rückwärtsgang von FB
};
Fernbedienung fb;

// ausgabe zum Motor controller
struct Motor
{
const byte pinvor = 9;     //pin vorwärtsgang ausgabe zum relais
const byte pinzur = 10;    //pin rückwärtsgang ausgabe zum relais
const byte power = 7;      //schaltet den controller ein und aus (könnte man noch als notaus benutzen)
int schub = 0;             //variable schub ausgang zum motor
bool vor = 0;              //variable vorwärtsgang
bool zur = 0;              //variable rückwärtsgang
};
Motor motor;


// ------------------------------------------------------------------------------------------------------------------------------------------------------------


void setup() {

  Serial.begin(19200); //usb schnitstelle monitor

//karosserie knüppel und nitro initialisieren
  pinMode(karosserie.pinvor, INPUT_PULLUP);
  pinMode(karosserie.pinzur, INPUT_PULLUP);
  pinMode(karosserie.pinnitro, INPUT_PULLUP);

//relais initialisieren
  pinMode((motor.pinvor), OUTPUT);
  pinMode((motor.pinzur), OUTPUT);

//Controller initialisieren
  pinMode((motor.power), OUTPUT);

}

//-----------------------------------------------------------------------------------------------------------------------------------


void loop() {


//gaspedal
  gaspedal.vargaspedal = analogRead(gaspedal.pin)/4-gaspedal.nullpunkt; //korrigiertes gaspedal (es kommt etwa -5 dabei raus)
  
  //nullkorrektur gaspedal
  if (gaspedal.vargaspedal<0) {                                         //vargaspedal wird auf 0 gesetzt (ist gewollt, da man ganz leicht das Pedal drücken muss um ein schub zu erzeugen)
    gaspedal.vargaspedal=0;
  }

  gaspedal.schub = gaspedal.vargaspedal/gaspedal.teiler;                //vargaspedal input (vollgas ist 157) / gpteiler = GPschub (geht etwas über 255 hinaus)

  //begrenzer gaspedal
  if (gaspedal.schub > 255) {                                           //maximaler schub wird auf 255 begrenzt
    gaspedal.schub =255;
  }

  //drossel & nitro gaspedal
  

  if (digitalRead(karosserie.pinnitro) == 1) {             //wenn nitro nicht gedrückt, GPschub auf auf GP drossel begrenzt (das auto ist ohne drossel zu schnell und somit eigendlich ein KFZ)
    if (gaspedal.schub>gaspedal.drossel) {
      gaspedal.schub = gaspedal.drossel;
    }
  }






//Karosserie Knüppel

  if (digitalRead(karosserie.pinvor) + (digitalRead(karosserie.pinzur)) == 2) {  //leerlauf erkennung
    karosserie.KNvor = 0;
    karosserie.KNzur = 0;
  }

  if (digitalRead(karosserie.pinvor) == 0) {    //einlegen vorwärtsgang
    karosserie.KNzur = 0;
    karosserie.KNvor = 1;
  }

  if (digitalRead(karosserie.pinzur) == 0) {    //einlegen rückwärstgang
    karosserie.KNvor = 0;
    karosserie.KNzur = 1;
  }

  if (digitalRead(karosserie.pinvor) + (digitalRead(karosserie.pinzur)) == 0) {  //störung beide gänge gleichzeitig
    karosserie.KNvor = 0;
    karosserie.KNzur = 0;
  }


  
//fernbedienung
  
  //signal auslesen
  fb.in = pulseIn(fb.pin, HIGH, 25000);               //dekodierung empfängerpin  in Live signal
  fb.pulsezeit = fb.in;  	                            //pulzeit von empfänger zu variable fb.pulsezeit

  //FB Entstörung
  if (fb.in<fb.stoerL) {                              //wenn das Signal von FBin außerhalb der Tolleranz fb.stoerL oder fb.stoerH ist, wird fb.pulsezeit auf nullpunkt (1230) gesetzt
    fb.vor = 0;
    fb.zur = 0;
    fb.schub = 0;
    fb.pulsezeit = 1230;
  }


  if (fb.in>fb.stoerH) {
    fb.vor = 0;
    fb.zur = 0;
    fb.schub = 0;
    fb.pulsezeit = 1230;
  }




  //mittelwertbestimmung mit RunningMedian.lib um weitere störungen zu filtern

  samples.add(fb.pulsezeit);

  fb.pulsezeit = samples.getMedian();            //mittelwert wird als fb.pulsezeit zurück gegeben


  //erkennung leerlauf, richtung und schub von Fernbedienung

  if (fb.pulsezeit<fb.nullvor) {                   //erkennung leerlauf
    if (fb.pulsezeit>fb.nullzur){
      fb.schub = 0;
      fb.vor = 0;
      fb.zur = 0;
    }
  }
  //vorwärts
  if (fb.pulsezeit >fb.nullvor) {                  //entstörtes und gemitteltes Signal wird mit den Nullpunkt Variablen verglichen/umgerechnet, vor- oder rückwärtsgang eingelegt und fb.schub geschrieben sowie auf max 255 begrenzt
    fb.zur = 0;
    fb.vor = 1;
    fb.schub = fb.pulsezeit-fb.nullvor;
    fb.schub = fb.schub/2;
  }
  //rückwärts
  if (fb.pulsezeit <fb.nullzur) {
    fb.vor = 0;
    fb.zur = 1;
    fb.schub = fb.nullzur-fb.pulsezeit;
  }
  //schubbegrenzung
  if (fb.schub>255) {
    fb.schub =255;
  }
  if (fb.schub<0) {
    fb.schub = 0;
  }

//MOTORCONTROLLER

  //C-vorwärts
  if (fb.vor>0) {                      //wenn ein vorwärtssignal von Fernbedienung kommt, werden alle anderen Variablen überschrieben und vorwärtsgang eingelegt
    karosserie.KNvor = 0;
    karosserie.KNzur = 0;
    motor.zur = 0;
    motor.vor = 1;  
  }
  else {
    motor.vor = karosserie.KNvor;     //kein signal von Fernbedienung, Karosserie Knüppelstellung wird übernommen
  }

  //C-rückwärts
  if (fb.zur>0) {                     //wenn rückwärtssignal von Fernbedienung kommt, werden alle anderen Variablen überschrieben und rückwärtsgang eingelegt
    karosserie.KNzur = 0;
    karosserie.KNvor = 0;
    motor.vor = 0; 
    motor.zur = 1; 
  }
  else {
    motor.zur = karosserie.KNzur;     //kein signal von Fernbedienung, Karosserie Knüppelstellung wird übernommen
  }

  //C-schub
  if (fb.schub>5) {                   //wenn an der Fernbedienung merkbar Gas gegeben wird, wird Gaspedal auf 0 gesetzt und fb.schub auf motor.schub geschrieben
    motor.schub = fb.schub;
    gaspedal.schub = 0;
  }
  else {
    motor.schub = gaspedal.schub;     //kein Schubsignal von der Fernbedienung, motor.schub wird vom gedrosseltem Gaspedal übernommen
  }


//Relaismodul

  //leerlauf
  if (motor.vor+motor.zur == 0) {
    digitalWrite(motor.pinvor, HIGH);
    digitalWrite(motor.pinzur, HIGH); 
    delay(80);
  }

  //Störung vorwärts und rückwärts gleichzeitig eingelegt
  if (motor.vor+motor.zur == 2) {
    digitalWrite(motor.power, HIGH);    //controller wird zum schutz ausgeschaltet
    digitalWrite(motor.pinvor, HIGH);
    digitalWrite(motor.pinzur, HIGH);
    delay(200); 
  }  

  //motor vorwärts
  if (motor.vor == 1) {
    digitalWrite(motor.pinzur, HIGH);
    delay(80);
    digitalWrite(motor.pinvor, LOW); 
  }

  //motor rückwärts
    if (motor.zur == 1) {
    digitalWrite(motor.pinvor, HIGH);
    delay(80);
    digitalWrite(motor.pinzur, LOW); 
  }

analogWrite(5, motor.schub);  //es wird schubbefehl an motor geschriieben



//monitor ausgaben

  Serial.print("  GP: ");Serial.print(gaspedal.vargaspedal);
  Serial.print("  GPschub: ");Serial.print(gaspedal.schub);
  Serial.print("  KNvor: ");Serial.print(karosserie.KNvor);
  Serial.print("  KNzur: ");Serial.print(karosserie.KNzur);
    Serial.print("\t");Serial.print("\t");
  Serial.print("  FBin: ");Serial.print(fb.in);
  Serial.print("  FB: ");Serial.print(fb.pulsezeit); 
  Serial.print("  FBschub: ");Serial.print(fb.schub);
  Serial.print("  FBvor: ");Serial.print(fb.vor);
  Serial.print("  FBzur: ");Serial.print(fb.zur);
    Serial.print("\t");Serial.print("\t");
  Serial.print("AUSGABEN:");
  Serial.print("\t");
  Serial.print("  motor.vor: ");Serial.print(motor.vor);
  Serial.print("  motor.zur: ");Serial.print(motor.zur);
  Serial.print("  motor.schub: ");Serial.print(motor.schub);



 //nicht löschen!!
  Serial.println(" ");
  digitalWrite(motor.power, LOW);
}

Wer bis hier her durchgelesen hat, vielen dank :D

Hallo,

ich kann hier nur meine Meinung wiedergeben.

Gewöhne dir lieber das Pluszeichen für Vergleiche ab. Das könnte einmal nach hinten losgehen. Habe zwar gerade keinen Beweis dafür zur Hand, ist aber eine Addition und kein logischer Vergleich. Nimmer lieber boolsche Operatoren

Zum definieren der Fahrtrichtung 2 Motorvariablen empfinde ich für gefährlich. Mein Auto hat nur einen Ganghebel der nur einen Zustand zum Zeitpunkt x haben kann. :wink: Irgendwo im Programm eine Änderung übersehen und es wird hoffentlich gleich entdeckt oder zu spät ...

Damit wären wir eigentlich schon beim nächsten Tipp. Meinem Liebling enums und switch case.
Eine Variable mit sprechenden Namen und sprechenden Zuständen. Eine Variable die mehrere Zustände haben kann.

Ein int var könnte auch mehrere Werte besitzen. -1111 oder 3 oder 21345. Mit enum kann man jedoch sprechende Werte definieren. FORWARD, BACKWARD, NEUTRAL, STOP und das switch case wertet das aus. Mach dich mal schlau über enum und switch case. Vielleicht kannste das schon selbst anwenden. :wink: Wenn dann irgendwelche Variablen außerhalb von Limits liegen wird der enum Variablen bspw. STOP zugewiesen und das switch case bleibt in dem Zustand bis der Fehler behoben wurde. Wie das aussieht ist erstmal zweitrangig. Kann vom kompletten Controller Reset bis irgendein anderer Eingriff alles sein.

Ich meine man kann das auch ganz einfach haben. int var und man definiert sich -1 für rückwärts, 1 für vorwärts und 0 für Stopp. Kann man halten wie man will. Bei enum wiederum kann man nichts anderes verwenden wie das selbst definierte. Beim int var kann man auch Mist zuweisen.

Danach kannste dir überlegen alle Vergleiche in so eine Schrittkette zu verlagern. Hängt auch viel vom eigenen Programmierstil ab. Der nächste Schritt wäre paar Funktionen zu schreiben. Oder erst Funktionen und dann enum switch case. Wie du magst. Wenn der Sketch nicht weiter wächst wäre da so ohne Funktionen geradeso noch vertretbar. Der Programmierstil verändert sich sowieso im Laufe der Zeit je mehr man programmiert. Eins kommt nach dem Anderen.

2 Likes

Guten abend,

habe mir heute mal ein bisschen enum und switch case durchgelesen, aber noch nicht ausprobiert.
wenn ich es richtig verstanden habe, wäre es ein eleganter weg, das ich für die karosserie.(vor/zur), sowie die anderen richtungsvariablen.
eine einzige

enum bewegung(vor,zur,leer,stop)

anlege.
Diese enum bewegung wird innerhalb des sketch durchlaufs, zuerst vom Knüppel und danach von der Fernbedienung als state/case verarbeitet/geschrieben (Fernbedienung überschriebt die auswertung vom knüppel), wenn fernbedienung im leerlauf wird knüppel übernommen.

Nach verarbeitung wird die enum bewegung dann in einem switch case

switch(bewegung) {
	case vor:     
    digitalWrite(motor.pinzur, HIGH);
    delay(80);
    digitalWrite(motor.pinvor, LOW); 
    ; break;
  (hier noch weitere fälle für zur/stop)
	
    	default: hier leerlauf; break;
}

auf den controller geschrieben.

Wenn ich das soweit richtig verstanden habe

Der nächste Schritt wäre paar Funktionen zu schreiben. Oder erst Funktionen und dann enum switch case.

Was darf ich mir darunter vorstellen?
Was wäre eine solche funktion?

An dieser stelle möchte ich mich auch nochmal für die wege die mir aufgezeigt werden, bedanken, ich weiß das zu schätzen, da ich finde das es ein sehr sinnvoller und elleganter weg ist.

Zitat:

Mein Auto hat nur einen Ganghebel der nur einen Zustand zum Zeitpunkt x haben kann

dem gibt es halt nichts hinzuzufügen.

Das sketch wird auch noch weiter wachsen, da ich nicht aufhöre, wenn es stabil fährt.
Ich hätte gerne noch so dinge wie einen Sound generator, ein oder zwei funktionierende anzeigen im Armaturenbrett.
Auch sind mir schon so Spinnereien wie eine Anti Schlupfregelung/ABS/ESP in den sinn gekommen.

Leider hab ich die nächsten Tage Spätschicht und wenig zeit zum rumprobieren, kann mir aber schon mal gedanken machen und im vorfeld lernen.

Dem Schnipsel entnehme ich:
Der Antrieb geht zurück an.
Das Ganze für 80ms pausiert.
Dann geht der Antrieb vorwärts aus.

Beginne alles ohne jegliches delay() zu schreiben.
Wenn Du etwas umschaltest, erst aus, dann an.
Du kannst das enum auch verwenden um HIGH und LOW als AUS und EIN zu definieren :wink:

Es ist etwas anders. Das sind nur richtungen, ein Schub wird anders an den Controller übergeben. der Rückwärtsgang wird ausgeschaltet, es werden 80ms gewartet, damit das Relais abfällt, dann geht der vorwärtsgang an.
Eine kleine wartezeit hätte ich da schon gerne, da insgesammt 4 Relais geschaltet werden müssen.
Pro richtung, jeweils ein relais am Relaismodul gesteuert vom Arduino und eins im Controller verbaut. (2 Relais nach einander)

Hallo,

Funktionen "kapseln" Code. Sie besitzen entweder Übergabeparameter und/oder einen Rückgabewert oder nichts von dem. Sie können wiederverwendbar sein oder auch nicht. Aber sie räumen den Code auf sodass man in der Hauptschleife den Überblick behält. Damit wird der gesamte Code auch wartbarer.

www.cpp-tutor.de
www.willemer.de

Eine Demo zum rumspielen. Bau dir serielle Ausgabe ein und schau was wie passiert.

const byte pinTaster {2};
const byte pinLed {12};
const byte pinHeartbeat {13};
const unsigned int BLINKPAUSE {1000};


void setup (void)
{
  pinMode(pinTaster, INPUT_PULLUP);
  pinMode(pinLed, OUTPUT);
  pinMode(pinHeartbeat, OUTPUT);
}

void loop (void)
{  
  steuerung(pinTaster, pinLed);
  heartbeat(pinHeartbeat, 500);
}


void steuerung (const byte pinT, const byte pinL)
{
  enum state {EIN, PAUSE_EIN, AUS, PAUSE_AUS, NICHTS};
  static state zustand {NICHTS};
  static unsigned long zeitmerker {0}; 
  
  switch (zustand) {
    case NICHTS:
                if (updateTaster(pinT) ) {
                  zustand = EIN;
                }
                break;

    case EIN:
                digitalWrite(pinL, HIGH);
                zeitmerker = millis();
                zustand = PAUSE_EIN;
                break;

    case PAUSE_EIN:
                if ( millis() - zeitmerker >= BLINKPAUSE) {
                  zustand = AUS;
                }
                break;

    case AUS:
                digitalWrite(pinL, LOW);
                zeitmerker = millis();
                zustand = PAUSE_AUS;
                break;

    case PAUSE_AUS:
                if ( millis() - zeitmerker >= BLINKPAUSE) {
                  zustand = NICHTS;
                }
                break;
                            
    default: zustand = AUS;   // sollte nie eintreten
             break;
  }
}


bool updateTaster (const byte pin)
{
  static unsigned long last_ms {0};
  static bool state {false};

  if (millis() - last_ms >= 30) {
    last_ms = millis();
    state = !(digitalRead(pin));
  }
  return state;
}


void heartbeat (const byte pin, const unsigned int interval)  // Kontrolle ob Sketch blockiert
{
  static unsigned long lastMillis {0};
  static bool state {LOW};
  const unsigned long ms {millis()};
  
  if (ms - lastMillis >= interval) {
    lastMillis = ms;
    state = !state;
    digitalWrite(pin, state);
  }
}

Die Funktionen updateTaster() und heartbeat() sind so wie gezeigt nicht wiederverwendbar. Nur für den einen Taster oder die eine Led verwendbar. Für mehrere Taster bspw. die Lib GitHub - thomasfredericks/Bounce2: Debouncing library for Arduino and Wiring installieren.

Wenn du dich in die Programmierung vertiefen möchtest und danach sieht es aus, dann empfehle ich ein Buch zu kaufen. In der Regel wird das von Ulrich Breymann empfohlen. "C++ programmieren" aktuell für C++20. Wenn du irgendwie noch die Vorgängerausgabe für C++17 billiger bekommst kannste auch das nehmen. Gibts als Buch und eBook. Vorgeschmack http://www.cppbuch.de/ Dann bekommst du alles erklärt. Auch das enum class sicherer ist wie nur enum usw. An das mehr an Syntax schreiben gewöhnt man sich. In C++ gehts primär um Typsicherheit und Datenkapselung. Das würde jetzt hier den Rahmen sprengen ein Buch zu erklären. Detailfragen können selbstverständlich jederzeit im Forum gestellt. Mach ich nicht anders.

Nochwas zu enum. Die Zustandsänderungen sollten sich alle innerhalb der "Steuerung" ändern. Nicht irgendwo verstreut im gesamten Programm. Besser erst gar nicht angewöhnen. Sieht man oft ist aber Mist.

Ist dieses buch gemeint?

Der C++-Programmierer: C++ lernen – professionell anwenden – Lösungen nutzen. Aktuell zu C++17: C++ lernen - professionell anwenden - Lösungen nutzen. Aktuell zu C++17. Extra: E-Book inside https://www.amazon.de/dp/3446448845/ref=cm_sw_r_apan_i_KRKZEWGJP5PFRDG7H8TJ

edit, hat sich erledigt. Ist bestellt.

Kurzes Update:

  • zweites Buch bestellt
  • code nochmal von Anfängerfehler bereinigt
  • code übersichtlicher gemacht
  • enum und case ausprobiert und gescheitert
  • Kurztripp mit Schwiegermutter und Frau gebucht :smiley:
  • hoffen, dass Amazon morgen nicht Streikt, damit ich am Nordseestrand in Holland was zu lesen habe.
  • die Grundplatte (für alle Steuergeräte) am Leiterrahmen gebohrt und befestigt
  • Bohrlöcher für den Motorcontroller gebohrt (hmpf keine M3 Schrauben, unterlegscheiben und Muttern mehr vorhanden)
  • Lenkgetriebe fertig gestellt und getestet, aber noch nicht mit Fahrtenregler der Lenkung verkabelt

neue Nebenaufgaben:

  • eine Veriegelung im Leerlauf über Fernbedienung (Idee vorhanden/umsetzung geplant) //das Schubsignal soll von fernbedienung gesperrt werden.

  • Wichtige ergänzung für nächsten Ausbauschritt -> 2 Parallele ( E-Bike-Akku 7s4p 24V 15ah Elektroroller Li-Ionen-Akku 18650 Akkus BMS )
    Ich brauch eine Spannungsüberwachung mit reduzierung der leistungsaufnahme, um im fall der fälle (niedrige Spannung) noch sicher heim zu kommen.

Hier nochmal der aktuelle Sketch, den ich hoffentlich von alle bößen no go´s befreit habe:

Anmerkung: für die, die schon mitlesen bitte besonderes augenmerk auf Teil zwischen

// ausgabe zum Motor controller und void setup()

//mittlerwert lib
#include <RunningMedian.h>
RunningMedian samples = RunningMedian(8);  //anzahl durchgänge für mittelwert



//gaspedal
struct GasPedal
{
const int drossel = 125;      //drossel gaspedal standart 125
const int nullpunkt = 55;     //nullpunkt gaspedal (vom Hallsensor wird 0,8V bzw 55 in nullstellung ausgegeben), wird zur berechnung des schubs 0-255 gebraucht
const float teiler = 0.6;     //gpteiler (gaspedal gibt 0 bis 156 aus, durch den teiler wird etwa 0 bis 260 ausgegeben)
const byte pin = A1;          //Gaspedal Inputpin
int vargaspedal = 0;          //variable gaspedal
int schub = 0;                //Schub von Gaspedal
};
GasPedal gaspedal;




//Karosse
struct Karosserie
{
const byte pinvor = 4;      //Pin vorwärtsgang
const byte pinzur = 6;      //Pin rückwärstgang
const byte pinnitro =2;     //Pin Nitroknopf
bool KNvor = 0;             //knüppel vorwärts
bool KNzur = 0;             //knüppel rückwärts
};
Karosserie karosserie;

// fernbedienung
struct Fernbedienung
{
const byte pin = 3;         //rc empfänger pin(Channel2 am empfänger) = pin 3 am arduino
const int nullvor = 1260;   //nullpunkt vorwärts (mittelwert, dh an der fernbedienung ist der knüppel in ruhestellung, etwa 1230)
const int nullzur = 1190;   //nullpunkt rückwärts (wird der knüppel über 1260 oder unter 1190 bewegt, registriert das program einen steuerbefehl)
const int stoerL = 900;     //störungswert unten 900 (pulsezeiten unter 900 oder über 1900 sind Funkstörungen)
const int stoerH = 1900;    //störungswert oben 1900
int in = 0;                 //fernbedienung live input
int pulsezeit = 0;          //rc empfänger pulsezeit enstört & korrigiert
int schub = 0;              //gasstellung zum motor
bool vor = 0;               //vorwärtsgang von FB
bool zur = 0;               //rückwärtsgang von FB
};
Fernbedienung fb;

// ausgabe zum Motor controller
struct Motor
{
const byte pinvor = 9;     //pin vorwärtsgang ausgabe zum relais
const byte pinzur = 10;    //pin rückwärtsgang ausgabe zum relais
const byte power = 7;      //schaltet den controller ein und aus (könnte man noch als notaus benutzen)
const byte schubpin = 5;   //PWM pin zum Controller
int schub = 0;             //variable schub ausgang zum motor
bool vor = 0;              //variable vorwärtsgang
bool zur = 0;              //variable rückwärtsgang
};
Motor motor;


//von hier aus ist alles Temporär im Forum Editor, aus meinem Kopf geschrieben



//HIER PLATZ FÜR ENUMS ???
(enum class Bewegung:int{vor = 1, zur = -1, leer = 0, stop = 5};   
//Anmerkung: ich hab noch keine ahnung aber das tutorial sagt so wird schön gemacht

// ------------------------------------------------------------------------------------------------------------------------------------------------------------


//HIER PLATZ FÜR UNTERFUNKTIONEN ???

void BewegDich (Bewegung bewegung) {

if (ich hier ahnung habe == false) 
bewegung::lese buch;
bewegung::frageUMhilfe;

else
   switch(bewegung)
 {
case bewegung::vor: 
    digitalWrite(motor.pinzur, HIGH);   //rückwärts aus
    delay(80);                                             //warte auf realais
    digitalWrite(motor.pinvor, LOW);    //vorwärts ein
  }

//das hier ist mein aktueller Lernstatus, gebe ich offen zu, ich hab mir das oben genannte Buch bestellt um zu verstehen was ich da mache. 
// wer noch nicht vor lachen auf dem boden liegt, ist sehr herzlich eingeladen was dazu zu schreiben




//bis hier ist alles Temporär im Forum Editor, aus meinem Kopf geschrieben

//rest vom code noch nicht auf neuen "Lern Status" umgeschrieben

// ------------------------------------------------------------------------------------------------------------------------------------------------------------
void setup() {

  Serial.begin(19200); //usb schnitstelle monitor

//karosserie knüppel und nitro initialisieren
  pinMode(karosserie.pinvor, INPUT_PULLUP);
  pinMode(karosserie.pinzur, INPUT_PULLUP);
  pinMode(karosserie.pinnitro, INPUT_PULLUP);

//relais initialisieren
  pinMode((motor.pinvor), OUTPUT);
  pinMode((motor.pinzur), OUTPUT);

//Controller initialisieren
  pinMode((motor.power), OUTPUT);
  pinMode((motor.schubpin),OUTPUT);

}

//-----------------------------------------------------------------------------------------------------------------------------------


void loop() {


//gaspedal
  gaspedal.vargaspedal = analogRead(gaspedal.pin) /4 - gaspedal.nullpunkt; //korrigiertes gaspedal (es kommt etwa -5 dabei raus)
  
  //nullkorrektur gaspedal
  if (gaspedal.vargaspedal < 0) {                                          //vargaspedal wird auf 0 gesetzt (ist gewollt, da man ganz leicht das Pedal drücken muss um ein schub zu erzeugen)
    gaspedal.vargaspedal = 0;
  }

  gaspedal.schub = gaspedal.vargaspedal / gaspedal.teiler;                   //vargaspedal input (vollgas ist 157) / gpteiler = GPschub (geht etwas über 255 hinaus)

  //begrenzer gaspedal
  if (gaspedal.schub > 255) {                                              //maximaler schub wird auf 255 begrenzt
    gaspedal.schub = 255;
  }

  //drossel & nitro gaspedal
  

  if (digitalRead(karosserie.pinnitro) == 1) {             //wenn nitro nicht gedrückt, GPschub auf auf GP drossel begrenzt (das auto ist ohne drossel zu schnell und somit eigendlich ein KFZ)
    if (gaspedal.schub > gaspedal.drossel) {
      gaspedal.schub = gaspedal.drossel;
    }
  }






//Karosserie Knüppel

  if (digitalRead(karosserie.pinvor) == 1 && (digitalRead(karosserie.pinzur)) == 1) {  //leerlauf erkennung
    karosserie.KNvor = 0;
    karosserie.KNzur = 0;
  }

  if (digitalRead(karosserie.pinvor) == 0) {    //einlegen vorwärtsgang
    karosserie.KNzur = 0;
    karosserie.KNvor = 1;
  }

  if (digitalRead(karosserie.pinzur) == 0) {    //einlegen rückwärstgang
    karosserie.KNvor = 0;
    karosserie.KNzur = 1;
  }

  if (digitalRead(karosserie.pinvor) == 0 && (digitalRead(karosserie.pinzur)) == 0) {  //störung beide gänge gleichzeitig
    karosserie.KNvor = 0;
    karosserie.KNzur = 0;
  }


  
//fernbedienung
  
  //signal auslesen
  fb.in = pulseIn(fb.pin, HIGH, 25000);               //dekodierung empfängerpin  in Live signal
  fb.pulsezeit = fb.in;  	                            //pulzeit von empfänger zu variable fb.pulsezeit

  //FB Entstörung
  if (fb.in < fb.stoerL) {                              //wenn das Signal von FBin außerhalb der Tolleranz fb.stoerL oder fb.stoerH ist, wird fb.pulsezeit auf nullpunkt (1230) gesetzt
    fb.vor = 0;
    fb.zur = 0;
    fb.schub = 0;
    fb.pulsezeit = 1230;
  }


  if (fb.in > fb.stoerH) {
    fb.vor = 0;
    fb.zur = 0;
    fb.schub = 0;
    fb.pulsezeit = 1230;
  }




  //mittelwertbestimmung mit RunningMedian.lib um weitere störungen zu filtern

  samples.add(fb.pulsezeit);

  fb.pulsezeit = samples.getMedian();            //mittelwert wird als fb.pulsezeit zurück gegeben


  //erkennung leerlauf, richtung und schub von Fernbedienung

  if (fb.pulsezeit < fb.nullvor) {               //erkennung leerlauf
    if (fb.pulsezeit > fb.nullzur){
      fb.schub = 0;
      fb.vor = 0;
      fb.zur = 0;
    }
  }
  //vorwärts
  if (fb.pulsezeit > fb.nullvor) {              //entstörtes und gemitteltes Signal wird mit den Nullpunkt Variablen verglichen/umgerechnet, vor- oder rückwärtsgang eingelegt und fb.schub geschrieben sowie auf max 255 begrenzt
    fb.zur = 0;
    fb.vor = 1;
    fb.schub = fb.pulsezeit-fb.nullvor;
    fb.schub = fb.schub/2;
  }
  //rückwärts
  if (fb.pulsezeit <fb.nullzur) {
    fb.vor = 0;
    fb.zur = 1;
    fb.schub = fb.nullzur-fb.pulsezeit;
  }
  //schubbegrenzung
  if (fb.schub > 255) {
    fb.schub = 255;
  }
  if (fb.schub < 0) {
    fb.schub = 0;
  }

//MOTORCONTROLLER

  //C-vorwärts
  if (fb.vor > 0) {                      //wenn ein vorwärtssignal von Fernbedienung kommt, werden alle anderen Variablen überschrieben und vorwärtsgang eingelegt
    karosserie.KNvor = 0;
    karosserie.KNzur = 0;
    motor.zur = 0;
    motor.vor = 1;  
  }
  else {
    motor.vor = karosserie.KNvor;     //kein signal von Fernbedienung, Karosserie Knüppelstellung wird übernommen
  }

  //C-rückwärts
  if (fb.zur > 0) {                     //wenn rückwärtssignal von Fernbedienung kommt, werden alle anderen Variablen überschrieben und rückwärtsgang eingelegt
    karosserie.KNzur = 0;
    karosserie.KNvor = 0;
    motor.vor = 0; 
    motor.zur = 1; 
  }
  else {
    motor.zur = karosserie.KNzur;     //kein signal von Fernbedienung, Karosserie Knüppelstellung wird übernommen
  }

  //C-schub
  if (fb.schub > 5) {                   //wenn an der Fernbedienung merkbar Gas gegeben wird, wird Gaspedal auf 0 gesetzt und fb.schub auf motor.schub geschrieben
    motor.schub = fb.schub;
    gaspedal.schub = 0;
  }
  else {
    motor.schub = gaspedal.schub;     //kein Schubsignal von der Fernbedienung, motor.schub wird vom gedrosseltem Gaspedal übernommen
  }


//Relaismodul

  //leerlauf
  if (motor.vor == 0 && motor.zur == 0) {
    digitalWrite(motor.pinvor, HIGH);
    digitalWrite(motor.pinzur, HIGH); 
    delay(80);
  }

  //Störung vorwärts und rückwärts gleichzeitig eingelegt
  if (motor.vor == 1 && motor.zur == 1) {
    digitalWrite(motor.power, HIGH);    //controller wird zum schutz ausgeschaltet
    digitalWrite(motor.pinvor, HIGH);
    digitalWrite(motor.pinzur, HIGH);
    delay(200); 
  }  

  //motor vorwärts
  if (motor.vor == 1) {
    digitalWrite(motor.pinzur, HIGH);
    delay(80);
    digitalWrite(motor.pinvor, LOW); 
  }

  //motor rückwärts
    if (motor.zur == 1) {
    digitalWrite(motor.pinvor, HIGH);
    delay(80);
    digitalWrite(motor.pinzur, LOW); 
  }


//motor schub
  //schubabschaltung leerlauf
  if ((motor.vor) == 0 && (motor.zur) == 0) {      //schubabschaltung gaspedal im leerlauf
    motor.schub = 0;
  }
  else {
    analogWrite(motor.schubpin, motor.schub);  //es wird schubbefehl an motor geschrieben
  }


//monitor ausgaben

  Serial.print("  GP: ");Serial.print(gaspedal.vargaspedal);
  Serial.print("  GPschub: ");Serial.print(gaspedal.schub);
  Serial.print("  KNvor: ");Serial.print(karosserie.KNvor);
  Serial.print("  KNzur: ");Serial.print(karosserie.KNzur);
    Serial.print("\t");Serial.print("\t");
  Serial.print("  FBin: ");Serial.print(fb.in);
  Serial.print("  FB: ");Serial.print(fb.pulsezeit); 
  Serial.print("  FBschub: ");Serial.print(fb.schub);
  Serial.print("  FBvor: ");Serial.print(fb.vor);
  Serial.print("  FBzur: ");Serial.print(fb.zur);
    Serial.print("\t");Serial.print("\t");
  Serial.print("AUSGABEN:");
  Serial.print("\t");
  Serial.print("  motor.vor: ");Serial.print(motor.vor);
  Serial.print("  motor.zur: ");Serial.print(motor.zur);
  Serial.print("  motor.schub: ");Serial.print(motor.schub);



 //nicht löschen!!
  Serial.println(" ");                    //zeilenumbruch Monitor
  digitalWrite(motor.power, LOW);         //controller wird nach dem ersten programmdurchlauf eingeschaltet
}

Wer Lust und Zeit hat sich das ganze erneut durchzulesen, ist herzlich eingeladen Anmerkungen und Kritik zu geben!

Ein besonderes dankeschön von mir geht an Doc_Arduino, michael_x & my_xy_projekt die mir viele fehler und anregeungen gegeben haben.

Grüße

AMORph01

P.S.:

Ich feiere Anmerkungen und Kritik, da ich daraus schönes neues gelernt habe, also nicht sparsam damit umgehen.

P.p.S.:

Ich feiere auch den überaus höflichen Umgang hier im Forum, gewohnt bin ich mit unter:

das ist schei#e
so ist das mist
alter, was hast du denn da gemacht
ZENSIERT
umgangsformen unter Handwerkern halt XD

Hallo,

hast du die Demo schon probiert? Wenn nicht, dann die harte Tour wenn du enum class schon kennengelernt hast. :wink: Der enum Datentyp ist wie immer standardmäßig int. Kannste locker auf byte ändern, spart etwas Flash oder man lässt es weg. Die Wertzuweisungen der Enum Parameter ist nicht sinnvoll. Das interessiert auch niemanden, weil wir arbeiten hier rein mit den sprechenden Namen. Das ist der Sinn dahinter. Um den Rest kümmert sich der Compiler. Ein case default ist mit enum class auch überflüssig gewurden, der Compiler meckert wenn ihm was nicht passt oder was vergessen wurde. In den IDE Voreinstellungen sollten alle Warnungen aktiviert sein.

const byte pinTaster {2};
const byte pinLed {12};
const byte pinHeartbeat {13};
const unsigned int BLINKPAUSE {1000};


void setup (void)
{
  pinMode(pinTaster, INPUT_PULLUP);
  pinMode(pinLed, OUTPUT);
  pinMode(pinHeartbeat, OUTPUT);
}

void loop (void)
{  
  steuerung(pinTaster, pinLed);
  heartbeat(pinHeartbeat, 500);
}


void steuerung (const byte pinT, const byte pinL)
{
  enum class state : byte {EIN, PAUSE_EIN, AUS, PAUSE_AUS, NICHTS};
  static state zustand {state::NICHTS};
  static unsigned long zeitmerker {0}; 
  
  switch (zustand) {
    case state::NICHTS:
                if (updateTaster(pinT) ) {
                  zustand = state::EIN;
                }
                break;

    case state::EIN:
                digitalWrite(pinL, HIGH);
                zeitmerker = millis();
                zustand = state::PAUSE_EIN;
                break;

    case state::PAUSE_EIN:
                if ( millis() - zeitmerker >= BLINKPAUSE) {
                  zustand = state::AUS;
                }
                break;

    case state::AUS:
                digitalWrite(pinL, LOW);
                zeitmerker = millis();
                zustand = state::PAUSE_AUS;
                break;

    case state::PAUSE_AUS:
                if ( millis() - zeitmerker >= BLINKPAUSE) {
                  zustand = state::NICHTS;
                }
                break;
  }
}


bool updateTaster (const byte pin)
{
  static unsigned long last_ms {0};
  static bool state {false};

  if (millis() - last_ms >= 30) {
    last_ms = millis();
    state = !(digitalRead(pin));
  }
  return state;
}


void heartbeat (const byte pin, const unsigned int interval)  // Kontrolle ob Sketch blockiert
{
  static unsigned long lastMillis {0};
  static bool state {LOW};
  const unsigned long ms {millis()};
  
  if (ms - lastMillis >= interval) {
    lastMillis = ms;
    state = !state;
    digitalWrite(pin, state);
  }
}
1 Like

Na das ist ja mal ne Aufforderung! :slight_smile:
Nu hab ich keine Every aber selbst das kompilieren geht nicht! :face_with_symbols_over_mouth:
Code sollte wenigstens das tun...
Grund:

Was macht die Klammer am Anfang der Zeile da? ...
Und alles andere was danach kommt, macht mich traurig, gerade weil enum und switch/case eigentlich recht einfach sind.

Aber, es es gibt Hoffnung, Dir auch noch zu helfen, was recht einfach umzusetzen ist.

A:) Verplemper nicht unnötig Bytes für Leerzeichen auf dem Seriellen Monitor! Das ist Kosmetik, die Du nie brauchst.
B:) Benutze ab drei auszugebenden Zeichen gerne das F() makro. (ganz nach unten scrollen)
Sieht dann so aus:

 Serial.print(F("GP: "));Serial.print(gaspedal.vargaspedal);

Und das hier:

braucht 2 vollkommen unnütze Bytes.
Da reicht ein

 Serial.println();

Na mal sehen, was draus wird...

1 Like

Vielen dank für die weiteren Anregungen.

Aus Gründen (siehe Bilder) konnte ich leider noch nichts testen und bin die nächsten Tage in Urlaub.


Ziel ereicht, alles ist mit seiner passenden Spannung versorgt.


Hätte nicht gedacht, das es so ein dicker Schinken ist.

Den hinweis mit der Konsole finde ich sehr schön, hab mir schon sowas gedacht, weil die hier und da ins stocken geraten ist.

Bis demnächst....

Da C++ eine der komplexesten/schwierigsten Sprachen überhaupt ist, ist das Buch eher noch zu dünn.
Viel Randgebiete, wie Arduino/Embeded erfasste es quasi gar nicht.
Die paar Seiten reichen gerade für die wichtigsten C++ Grundlagen.

This topic was automatically closed 180 days after the last reply. New replies are no longer allowed.