Projekt Schallerzeuger / Fragen zum Chaos

Hallo Alle,
ich baue gerade an einem Schallerzeuger für eine Halloween Figur.
Der Schallerzeuger funktioniert im Prinzip ähnlich wie eine Boga, in dem er die Verbrennungsgase auf Überschall beschleunigt, die am ende des Rohrs auf eine Wand aus stehender Luft treffen. So viel zum Prinzip.

Zu meinem Problem:
Der Schallerzeuger soll bei bestimmten Ereignissen einen Knall erzeugen
Nach dem Einschalten soll die erste LED durch langsames Blinken den "Bereitschaftszustand" anzeigen. Der Schallerzeuger wartet auf einen Tastendruck, der die Ladezeit bestimmt. Es gibt drei verschiedene Lademengen, wobei die erste nur Zimmerlautstärke erzeugt.
Nachdem eine der Tasten gewählt wurde, zieht der Servo für eine definierte Zeit ein Ventil und lässt Butangas durch eine Venturidüse in den Schallerzeuger (Signalisiert durch die 2. LED).
Ist die Zeit abgelaufen (Timeout 1, 2, 3) springt der Schallerzeuger in den Zustand "Zündbereit" und wartet auf die Feuertaste. Wird diese gedrückt, zieht ein relais an, dass einen Funkenerzeuger schält. Blitzbumm. Break;, Neustart.

Ich habe versucht (hat wie ihr seht nicht geklappt) auf delays zu verzichten. Und ich möchte lernen mit Strukturen zu arbeiten, deshalb die "cases".
Ich dachte, das ist für mich als Anfänger leichter zu verstehen, weil ich ähnlich wie damals in Basic (Nicht lachen jetzt, war meine erste Programmiersprache auf dem VC64 :wink: ) hin und herspringen k(önnen) wollte^^. Schade.
10
20
30

Das ganze war erstmal ein Griff ins Klo so, und ihr werdet mich auslachen^^
Ich bin hier, weil ich Hilfe brauche, es richtig zu machen.
Die Aufgabe ist eigentlich nicht schwer und wäre auch mit Delays und einfacher Abfolge von Befehlen möglich. Ich will das aber noch erweitern, deshalb würde ich das mit den Cases gerne so lassen irgendwie.

#include <Servo.h>

const int ledPin1 =  4;
const int ledPin2 =  3;
const int ledPin3 =  2;
const int buttonPinLoad1 = 12;   // Laden1
const int buttonPinLoad2 = 10;   // Laden2
const int buttonPinLoad3 = 9;   // Laden3
const int buttonPinFire = 11; // Feuer
Servo myservo;

const long time1 = 1000; // 1sec. testbang
const long time2 = 3000; // 3sec. soft
const long time3 = 6000; // 6sec. hard
int myState = 0;
long myTimeout = 0;
long myStartTime = 0;
void setup() {
  myservo.attach(6);  // attaches the servo on pin 9 to the servo obj
  pinMode(buttonPinFire, INPUT_PULLUP);
  pinMode(buttonPinLoad1, INPUT_PULLUP);
  pinMode(buttonPinLoad2, INPUT_PULLUP);
  pinMode(buttonPinLoad3, INPUT_PULLUP);

  Serial.begin(9600);
  myservo.write(90);
}
void loop() {
  switch (myState) {
    case 0:  // Warten
    blinkSlow1(); // LED Ausgabe langsames Blinken
      
      if (digitalRead(buttonPinLoad1) == 0) myState = 1;
      if (digitalRead(buttonPinLoad2) == 0) myState = 2;
      if (digitalRead(buttonPinLoad3) == 0) myState = 3;
      break;
  
    case 1: // ladestart 1

      if (digitalRead(buttonPinFire) == 1) {
        blinkFast1(); // LED1 Ausgabe langsames Blinken 
        blinkSlow2(); // LED2 Ausgabe langsames Blinken
        myTimeout = time1;
        myStartTime = millis();
        myservo.write(100);
        if (millis() - myStartTime > myTimeout) myState = 4;
      }
    case 2: // ladestart 2

      if (digitalRead(buttonPinFire) == 1) {
        blinkFast1(); // LED1 Ausgabe langsames Blinken 
        blinkFast2(); // LED2 Ausgabe schnelles Blinken
        myTimeout = time2;
        myStartTime = millis();
        myservo.write(100);
        if (millis() - myStartTime > myTimeout) myState = 4;
      }
    case 3: // ladestart 3

      if (digitalRead(buttonPinFire) == 1) {
        blinkFast1(); // LED1 Ausgabe schnelles Blinken 
        blinkFastest2(); // LED2 Ausgabe schnellstes Blinken
        myTimeout = time3;
        myStartTime = millis();
        myservo.write(100);
        if (millis() - myStartTime > myTimeout) myState = 4;
      }
    case 4: // Gas Aus
	  myservo.write(90);
      if (digitalRead(buttonPinFire) == 1) {
        blinkFast3(); // LED3 Ausgabe schnelles Blinken
        
        if (millis() - myStartTime > myTimeout) myState = 5;
      }
    
    case 5: // Feuerbereit
		
      if (digitalRead(buttonPinFire) == 1) {
        blinkFast2(); // LED3 Ausgabe schnelles Blinken
      }
    break;

}
}
void blinkFast1(){
  if (millis() % 500 > 250) analogWrite(ledPin1, 155);
  else analogWrite(ledPin1, 0);
}
void blinkFast2(){
  if (millis() % 500 > 250) analogWrite(ledPin2, 155);
  else analogWrite(ledPin2, 0);
  
}
void blinkFast3(){
  if (millis() % 500 > 250) analogWrite(ledPin3, 155);
  else analogWrite(ledPin3, 0);

}
void blinkFastest1(){
  if (millis() % 200 > 100) analogWrite(ledPin1, 155);
  else analogWrite(ledPin1, 0);
}
void blinkFastest2(){
  if (millis() % 200 > 100) analogWrite(ledPin2, 155);
  else analogWrite(ledPin2, 0);
  
}
void blinkFastest3(){
  if (millis() % 200 > 100) analogWrite(ledPin3, 155);
  else analogWrite(ledPin3, 0);
  
}
void blinkSlow1() {
  if (millis() % 2000 > 1000) analogWrite(ledPin1, 255);
  else analogWrite(ledPin1, 0);
  
}
void blinkSlow2() {
  if (millis() % 2000 > 1000) analogWrite(ledPin2, 255);
  else analogWrite(ledPin2, 0);
  
}
void blinkSlow3() {
  if (millis() % 2000 > 1000) analogWrite(ledPin3, 255);
  else analogWrite(ledPin3, 0); 
  

}

Ist so eine Konstruktion mit einer Gasexplosion nicht verboten?

Warum ? Also mal davon abgesehen, dass es nichts mit der Fragestellung zu tun hat.
Es ist keine Waffe, es gibt kein Projektil und es ist auch nicht ohne weiteres möglich, Projektile zu verwenden. Weil der Venturieffekt nur dann funktioniert, wenn dem Gemisch kein Widerstand entgegengebracht wird. Eine Weinbergkanone funktioniert z.B. ähnlich. Um Wild zu vergrämen. Ich erzeuge nur mit minimalem Kraftstoff einen lauten Knall. Im Prinzip mit einem Gasbrenner und einem Rohr. Das ganze lässt sich miniaturisiert auch in RC Modellen verwenden. Es ist nur eine Simulation^^

Was funktioniert nicht wie erwartet? Und was hast du erwartet?

Spontan fällt mir auf das myState nicht mehr den Wert 0 erreicht.

Ich werde aus einem Teil Deiner Kommentare zur Aufgabenstellung nicht schlau.
Das erste blinken bekomme ich noch hin.
Wenn eine Ladetatse gedrückt wird, nächster Step. Da schreibst Du aber:

Da passt der Kommentar nicht und ich sehe in der Beschreibung auch kein schnelles blinken der ersten led.

Im case 1-3 setzt Du myStartTime ständig neu.

Damit wird myTimeOut nie erreicht.

Hier auch Kommentar != Code:

Soll da led3 blinken oder led2?

Ich hab mir was zusammengereimt.

#include <Servo.h>

const byte ledPin1 =  4;
const byte ledPin2 =  3;
const byte ledPin3 =  2;
const byte buttonPinLoad1 = 12;   // Laden1
const byte buttonPinLoad2 = 10;   // Laden2
const byte buttonPinLoad3 = 9;   // Laden3
const uint32_t time1 = 1000; // 1sec. testbang
const uint32_t time2 = 3000; // 3sec. soft
const uint32_t time3 = 6000; // 6sec. hard
bool ledState1 = false;
bool ledState2 = false;
bool ledState3 = false;
uint32_t lastMillis1 = 0;
uint32_t lastMillis2 = 0;
uint32_t lastMillis3 = 0;
const uint32_t blinkSlow = 1000;
const uint32_t blinkFast = 500;
const uint32_t blinkFastest = 250;

const int buttonPinFire = 11; // Feuer
Servo myservo;


int myState = 0;
long myTimeout = 0;
long myStartTime = 0;
void setup()
{
  myservo.attach(6);  // attaches the servo on pin 9 to the servo obj
  pinMode(buttonPinFire, INPUT_PULLUP);
  pinMode(buttonPinLoad1, INPUT_PULLUP);
  pinMode(buttonPinLoad2, INPUT_PULLUP);
  pinMode(buttonPinLoad3, INPUT_PULLUP);
  Serial.begin(9600);
  myservo.write(90);
}
void loop()
{
  switch (myState)
  {
    case 0:  // Warten
      blink(ledPin1, blinkSlow, ledState1, lastMillis1);
      if (digitalRead(buttonPinLoad1) == LOW)
      {
        myStartTime = millis();
        myTimeout = time1;
        myState = 1;
      }
      if (digitalRead(buttonPinLoad2) == LOW)
      {
        myStartTime = millis();
        myTimeout = time2;
        myState = 2;
      }
      if (digitalRead(buttonPinLoad3) == LOW)
      {
        myStartTime = millis();
        myTimeout = time3;
        myState = 3;
      }
      break;
    case 1: // ladestart 1
      if (digitalRead(buttonPinFire) == 1)
      {
        blink(ledPin1, blinkSlow, ledState1, lastMillis1);
        blink(ledPin2, blinkSlow, ledState2, lastMillis2);
        myservo.write(100);
        if (millis() - myStartTime > myTimeout)
        {
          myStartTime = millis();
          myState = 4;
        }
      }
    case 2: // ladestart 2
      if (digitalRead(buttonPinFire) == 1)
      {
        blink(ledPin1, blinkFast, ledState1, lastMillis1);
        blink(ledPin2, blinkFast, ledState2, lastMillis2);
        myservo.write(100);
        if (millis() - myStartTime > myTimeout)
        {
          myStartTime = millis();
          myState = 4;
        }
      }
    case 3: // ladestart 3
      if (digitalRead(buttonPinFire) == 1)
      {
        blink(ledPin1, blinkFast, ledState1, lastMillis1);
        blink(ledPin2, blinkFastest, ledState2, lastMillis2);
        myservo.write(100);
        if (millis() - myStartTime > myTimeout)
        {
          myStartTime = millis();
          myState = 4;
        }
      }
    case 4: // Gas Aus
      myservo.write(90);
      ledAus(ledPin1, ledState1, lastMillis1);
      ledAus(ledPin2, ledState2, lastMillis2);
      if (digitalRead(buttonPinFire) == 1)
      {
        blink(ledPin3, blinkFastest, ledState3, lastMillis3);
        if (millis() - myStartTime > myTimeout)
        {
          myState = 5;
        }
      }
    case 5: // Feuerbereit
      if (digitalRead(buttonPinFire) == 1)
      {
        blink(ledPin3, blinkFastest, ledState3, lastMillis3);
      }
      else
      {
        //FIRE!
        ledAus(ledPin1, ledState1, lastMillis1);
        ledAus(ledPin2, ledState2, lastMillis2);
        ledAus(ledPin3, ledState3, lastMillis3);
        myState = 0;
      }
      break;
  }
}

void ledAus(const byte ledPin, bool &state, uint32_t &lastTime )
{
  digitalWrite(ledPin, LOW);
  state = false;
  lastTime = 0;
}


void blink(const byte pin, const uint32_t onTime, bool &state, uint32_t &lastTime)
{
  blink(pin, onTime, state, lastTime, 255);
}
void blink(const byte pin, const uint32_t onTime, bool &state, uint32_t &lastTime, const byte pwm)
{
  if (millis() - lastTime > onTime)
  {
    state = !state;
    lastTime = millis();
  }
  analogWrite(pin, state ? pwm : 0);
}

Ihr zwei habt mir schon sehr geholfen.
Ich erkläre nochmal den Ablauf, wie er sein soll.
Es ist das erste mal, dass ich mit "Zuständen" arbeite.
Das "Blinken" ist nur ein Anhaltspunkt für mich, da ich das im Moment mit Tinkercad teste.
Später ersetzt das schnelle Blinken der 3., weissen LED, ein Relais mit "high".
Die Hardware ist zwar schon verkabelt, aber warum Bauteile grillen^^

Ich kann es leider erst heute Mittag testen.
Ablauf
0. Check ob Feuertaste versehentlich nicht noch gedrückt ist, Bereitschaftsblinken auf LED 1falls nicht, Statusänderung zu 1, sonst 0

  1. Warten auf Taste 1,2 oder3, Sprung zum jeweiligen Timer
    2.Servo macht Feuerzeugventil (ein Brenner) für eine bestimmte Zeit auf, anzeige des Ladens durch blaue LED
  2. fertig geladen (Timer), Servo schließt Gaszufuhr, Warten auf Feuertaste, 3 LED signalisiert Feuerbereit
  3. Feuer: Ein Relais (nicht im Sketch) zieht an und aktiviert einen Stepup mit 5kV um das Gemisch in den drei hintereinander gelagerten Brennkammern zu zünden. Boom. Fertig. Wieder Status 0

Ich teste deinen Code nachher, vielen Dank !
Sieht schon mal gut aus, aber zum verstehen werde ich ne Stunde Ruhe brauchen.
Bis heute Abend :wink: THX

Ja geil. Habs kurz in mein Tinkercad eingefügt, so in etwa hast du das richtig erraten^^
Jetzt muss ich verstehen. THX. Ich freu mich voll. Und so geordnet^^ Ich bin so ein kleiner Autist und ich will auch diese Struktur lernen.

Also. Funktioniert noch nicht ganz, aber wenn ich das, was du da geschrieben hast verstanden habe :smiley: , zuckt bestimmt auch der Servo nicht mehr rum und macht das was ich will. Bestimmt nur ein logisches Problem.

Was fehlt, bzw. geht nicht?

Sorry. Hatte wenig Zeit zu schauen heute. Der servo soll ja einmal kurz anziehen für die jeweilige Timeoutzeit und dann wieder zurückgehen. Er zuckt (in der Simulation) aber. Die Gasmenge soll ja definiert sein. Der Test (Timeout1) ist mehr ein "whoof", Timeout2 ist etwa so laut wie ein kleiner Chinaböller und Timeout3 ist schon ein Kanonenschlag. Alles darüber kannst zur Kommunikation mit der Nachbarstadt nutzen und ist für meinen Zweck eher ungeeignet. Eventuell, falls wir mal eine Hochzeit auf einer Burg oder ähnliches haben, um die alten Kanonen ohne Gefahr zu simulieren. Ich kann hier leider kein Video hochladen. Der Trend geht halt zu Umweltfreundlich.

Projekt:

Ihr kennt das Jawduino Projekt in seinen verschiedenen Ausführungen ?
Ich habe zwei von diesen Schädeln. Ich hänge bei diesem Projekt ("Schädulatur") leider auch fest.

Die Augen (zwei Tischtennisbälle) lassen sich mit jeweils drei aufgemalten Pupillen über ein Legogetriebe für oben, mitte und unten (9 positionen) steuern. Läuft im Moment über einen 2. Arduino. Ich würde die (zufällige) Augenbewegung gerne immer dann auslösen, wenn der sensor keine Spannung (Sprache) erkennt.

Dazu kommt noch ein Hund... Belesene oder Theatermenschen ahnen es:
Die Schädel sind Dr. Faust und Mephistoteles. Der "Hund" ist ein Stück zappelndes Kunstfell, dass dann mit einem Knall wegfliegt und... Schädel nummer 2 taucht auf.
Dazu der "Miniböller".
Ich habe 2 EqualizerLED Module verbaut, die mit 3 Bit jeweils die Linke und Rechte Tonspur triggern.So ist der Text von Dr. Faust Rechts, Mephistoteles Links und sie spielen Goethes Faust. So der Plan.

Hier aber nur der Schallerzeuger "Hund". Sonst wirds zu kompliziert. Der wird eh von Hand ausgelöst.

Hi !
Mittlerweile hab ich auch noch einen 3D Drucker. Scheiß ADHS. Wieder mal 3 Projekte gleichzeitig.
Also. Ich habe mich heute etwas mit dem Code auseinandergesetzt, manches muss ich erst noch nachlesen. Z.B. was const uint32_t genau tut habe ich (noch) nicht verstanden. Die englische Erklärung dazu bedarf einer Erklärung. Bissel. Bin aber noch neu in der Sache und eher learning by doing Mensch.
Unter
Login | Tinkercad habe ich das Projekt mal testweise "aufgebaut" (wer mal gucken mag :wink:

Mein Problem ist das "Servozucken", wohl ausgelöst durch den loop.
Der Servo soll aus der Ruheposition eine bestimmte Zeit (je nach Knopfdruck) auf eine Position fahren, um über ein Ventil gas in die Venturidüse zu blasen.Nach Ablauf der Zeit soll er wieder in die Ruheposition fahren. Ein "Rückschlagschutz" wird rein mechanisch realisiert, dient aber eher dazu, die Venturidüse zu schützen.
Eigentlich ist es simpel, zumindest, wenn man delays benutzt^^
Das will ich aber grundsätzlich nicht, weil ich die Möglichkeiten die diese Statusabfragen unabhängig von Zwangspausen bieten, sehe.
THX für eure Zeit soweit

Erstellt eine Konstante (const) vom Typ uint32_t. u steht für unsigned, also ohne Vorzeichen. int steht für Ganzzahl. 32 steht für die Bitanzahl die die Zahl groß ist. Somit kann man in der Konstante eine Zahl von 0 bis 4.294.967.295 hinterlegen.

1 Like

Ich habe mir angewöhnt u.a. (un)signed int bzw. (un)signed long als das zu schreiben, was es ist 'uint16_tbzw. uint32_t`.

Grund war, das INT unterschiedlich Speicher belegen kann.
Auf einem ESP ist int 4 byte breit, auf einem AVR 2 byte.
Ausserdem schreibt sich das schneller.
Wie sich die Kurz- und Langschreibweise auswirkt:

void setup() {
  Serial.begin(115200);
  Serial.println(F("Start..."));

  Serial.println(F("Belegter Speicher für:"));
  Serial.print(F("uint8_t: "));
  Serial.print(sizeof(uint8_t));
  Serial.println(F(" byte"));
  Serial.print(F("byte: "));
  Serial.print(sizeof(byte));
  Serial.println(F(" byte"));
  Serial.print(F("uint16_t: "));
  Serial.print(sizeof(uint16_t));
  Serial.println(F(" byte"));
  Serial.print(F("unsigned int: "));
  Serial.print(sizeof(unsigned int));
  Serial.println(F(" byte"));
  Serial.print(F("uint32_t: "));
  Serial.print(sizeof(uint32_t));
  Serial.println(F(" byte"));
  Serial.print(F("unsigned long: "));
  Serial.print(sizeof(unsigned long));
  Serial.println(F(" byte"));
}
void loop() {
  // put your main code here, to run repeatedly:

}

Das const sorgt nur dafür, das der Compiler das als unveränderbar anerkennt und Du im Code nicht durch Unachtsamkeit verscuhst den Inhalt zu ändern....

1 Like

Programm aus #14 mit UNO:

Start...
Belegter Speicher für:
uint8_t: 1 byte
byte: 1 byte
uint16_t: 2 byte
unsigned int: 2 byte
uint32_t: 4 byte
unsigned long: 4 byte

ESP32:

Start...
Belegter Speicher für:
uint8_t: 1 byte
byte: 1 byte
uint16_t: 2 byte
unsigned int: 4 byte
uint32_t: 4 byte
unsigned long: 4 byte

Dazu benötigt man ein Login, geht also nicht einfach mal so.

1 Like

Hilft auch dem Compiler beim Optimieren. In der Regel wird dafür dann gar keine Variable im RAM angelegt, der auf kleinen AVR Controllern sehr knapp ist.

1 Like

:wink:
Danke!

Jupp. Danke für die Ergänzung.

Warum eigentlich in der Regel? Eigentlich doch nur wenn die Konstante Global ist. Wenn die Konstante Lokal existiert, zum Beispiel in einer Funktion, kann sie jederzeit einmalig einen Wert einer Variable übernehmen. Dann sollte sie doch eigentlich im RAM abgelegt sein.

Ich denke lokale Konstanten kommen häufiger vor als globale Konstanten. Daher sind sie doch in der Regel eher im RAM?

Oder bin ich auf dem Holzweg?

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