AVR ATTiny13A Problem mit Servo + LED-Ansteuerung

OP #7571252
Lesenswert?

Hallo liebes Forum, ich hoffe ihr könnt mir weiterhelfen, da ich nach vielen vergeblichen Teststunden nicht mehr weiterweiß. Eigentlich sollte mein Vorhaben recht einfach sein...

Ziel: 4x 2xLEDs "zufällig" an/aus-schalten und einen Servo ebenfalls "zufällig" einen kurzen hin-und-her-Zyklus (Endstellungen) durchlaufen lassen.

Problem: Wenn alle LEDs-Codeabschnitte akiv sind, flackern die LEDs 2-4 (scheinbar unregelmäßiges 2ms und 3,5 ms-An/aus). LED 1 flackert zwar nicht, hat aber einen falschen Takt (8, 18 Sek....) und der Servo wird zwar synchron zur LED 3 angesteuert, aber nur mit einem 1ms-Signal anstatt eines Zyklus (Soll:1ms, 1 Sek. warten, ~2ms). Wenn nur der Code für die LED 3 (inkl. Servoaktivierung) aktiv ist, funktioniert es.

Vermutung: Da ich den Schaltungsaufbau & myC und Fuses bereits erfolgreich mit einem nur 4-LEDs-"zufällig"-an/ausschalten-lassen-Programm und einem Servo-Testprogramm (sekündlich zwischen Endstellungen umschalten) verwendet habe, verdächtige ich diese eher nicht. Ich vermute, dass ich bei meiner Timer-Konstruktion etwas nicht beachtet/falsch gemacht habe oder dass es im myC irgendwelche Abhängigkeiten gibt, die mir nicht bewusst sind. Das Servosignal funktioniert prinzipiell mit 1+16 ms ganz gut. Trotzdem habe ich den Verdacht, dass beim Takt/Teiler etc. ggf. etaws nicht stimmt. Mit dem Datenblatt habe ich mich zwar bereits recht intensiv bei der Timer-Programmierung beschäftig, aber ich hoffe, dass ihr mir noch hilfreiche Tipps geben könnt. MfG Chilibit

myC: ATTiny 13A

Zu mir: ambitionierter Anfänger, habe aber schon 2 andere Projekte mit dem 13A umgesetzt (aber noch nichts mit Servos) ;-)

Servo: no name...

Ich habe Bilder der Fuses, Signalen an den Ausgängen und der Schaltung angehängt.

Microchip Studio 7.0.2594

Program Memory Usage: 540 bytes 52,7 % Full Data Memory Usage: 59 bytes 92,2 % Full

Programm:

1
#define F_CPU 9600000  //CPU auf 9,6MHz
2
#include <avr/io.h>
3
#include <stdlib.h>
4
#include <avr/interrupt.h>
5

6
//Augen-Zeitvariablen:
7
uint8_t zeitAugenFst=0;
8
uint8_t zeitAugenEinsSek=0;
9
uint8_t zeitAugenZweiSek=0;
10
uint8_t zeitAugenDreiSek=0;
11
uint8_t zeitAugenVierSek=0;
12

13
//Servo-Code:  
14
  uint8_t phase=0;
15
  uint8_t ZeitZaehlerServo=0;
16
  // Timer ist übergelaufen (alle 0,213ms (=256*0,000833ms)), neue Phase beginnt (?? Irgendwie fehlt hier noch der Faktor 10x um auf 17ms zu kommen?!)
17
  // Jede 8. Phase (~17,1ms) wird PB0=1 gesetzt und dann bei Erreichen des Vergleichswertes (OCR0A in der Main-Schleife löst "ISR(TIM0_COMPA_vect)" aus),s.u., wieder auf 0 gesetzt
18
  ISR(TIM0_OVF_vect)
19
  {
20
    // Servo nur in Phase 0 ansteuern
21
    if (phase==0)
22
    {
23
      PORTB |=(1<<PB0); //Servo-Signal an
24
      
25
      ZeitZaehlerServo = ZeitZaehlerServo+1;  //alle 8 Phasen (17 ms) eins hochzählen
26
      
27
      //Reduziere alle Zeitvariablen um 1 (59 => 0,97Sek)      
28
      zeitAugenFst = zeitAugenFst+1;
29
      
30
    }
31
    
32
    phase = phase+1; // Nächste Phase 1... 2...8 => 0
33
    if (phase > 8)  
34
    {
35
      phase=0;      
36
    }
37
  }
38

39
  // Timer hat Vergleichswert erreicht
40
  ISR(TIM0_COMPA_vect)
41
  {
42
    PORTB &= ~(1<<PB0); //Servo-Signal aus (=>~ 1 - 2 ms)
43
  }
44
  
45

46

47
uint8_t laufZeit=0;
48
uint8_t servoZyklus=0;
49
//Zur Info: Max-Werte für Zeit: 245 (x+10<256), Anzahl: 50
50
//const unsigned char zeitWerteSekundaer[50] = {52, 99, 167, 3, 55, 22, 119, 27, 153, 85, 22, 10, 103, 48, 24, 124, 11, 7, 57, 120, 47, 77, 138, 79, 20, 116, 52, 5, 191, 8, 153, 26, 72, 64, 18, 185, 30, 12, 3, 129, 112, 17, 197, 89, 15, 107, 2, 61, 164, 79};
51
const uint8_t zeitWerteSekundaer[50] = {1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2};
52

53
uint8_t zufallsZeitWert();
54

55
int main(void)
56
{      
57
  //Servo-Code
58
    cli(); // Loesche globales Interruptflag
59
    //Setzen der Register fuer 20 ms Timerinterrupt
60
    TCNT0 = 0; //Loesche Timer Counter 0
61
    // Starte Timer 0 im Fast PWM Modus mit Prescaler 256
62
    TCCR0A=0;   //Loesche Timer Counter Controll Register A
63
    TCCR0A |= (1<<WGM01) | (1<<WGM00); //=> Fast PWM
64
    TCCR0B=0;   //Loesche Timer Counter Controll Register B
65
    TCCR0B |= (1<<CS01);  //=>Prescaler 8
66
    //TCCR0B |= (1<<CS02);  //=>Prescaler 256
67
    // Erlaube Interrupts
68
    //Timer/Counter Interrupt Mask Register:
69
    TIMSK0=0;
70
    TIMSK0 |= (1<<OCIE0A) | (1<<TOIE0);
71
    sei();    //Setzen des globalen Interrupt-Enable-Bit
72
    //DDRB   = 0x1f;                    // Eingänge: Reset PB5; Ausgänge: PB0-PB4 (0: Servo; 1-4: LED-Augen 1-4)
73
    DDRB |= (1<<PB4) + (1<<PB3) + (1<<PB2) + (1<<PB1) + (1<<PB0);
74

75

76

77
  while (1) 
78
    {      
79
    //Zeitabfrage Augen 1 bis 4 (Servo mit Auge-3-Anschaltung synchronisiert)    
80
    if(zeitAugenFst>59){ //Sekundentaktgeber
81
      //Sekunden-Taktgeber zurücksetzen
82
      zeitAugenFst = 0; //59 entspricht ca. 1 Sek. (0,97s)
83
      
84
      
85
      //Auge 1: zweite Zeitvariable prüfen (Anzahl an Sekunden)
86
      if (zeitAugenEinsSek>10)
87
      {                
88
        zeitAugenEinsSek--; //Sekundären Zeitzähler reduzieren
89
      }
90
      else
91
      {
92
        //Wenn beide Zeitvariablen abgelaufen sind:
93
        //wenn Auge an, dann ausschalten
94
        if (PINB & (1<<PINB1)) //wenn PinB1 =1
95
        {
96
          //ausschalten
97
          PORTB &= ~(1<<PB1); // PinB1 auf 0 setzen          
98
          //Zeitvariable auf Zufallswert für Aus-Zeit setzen          
99
          zeitAugenEinsSek = 10 + zufallsZeitWert();//Wert aus Zufallsliste beziehen
100
        }
101
        else {
102
          //wenn Auge aus, dann anschalten
103
          PORTB |= (1<<PB1); // PinB1 auf 1 setzen          
104
          //Zeitvariable auf Zufallswert für An-Zeit setzen          
105
          zeitAugenEinsSek = 10+ zufallsZeitWert();//Wert aus Zufallsliste beziehen          
106
        }
107
      }
108
      
109
      
110
      
111
      //Auge 2: zweite Zeitvariable prüfen (Anzahl an Sekunden)
112
      if (zeitAugenZweiSek>10)
113
      {
114
        //Sekundären Zeitzähler reduzieren
115
        zeitAugenZweiSek--;
116
      }
117
      else
118
      {
119
        //Wenn beide Zeitvariablen abgelaufen sind:
120
        //wenn Auge an, dann aus schalten
121
        if (PINB & (1<<PINB2)) //wenn PinB2 =1
122
        {
123
          //ausschalten
124
          PORTB &= ~(1<<PB2); // PinB2 auf 0 setzen
125
          //Zeitvariable auf Zufallswert für Aus-Zeit setzen          
126
          zeitAugenZweiSek = 10+ zufallsZeitWert();//Wert aus Zufallsliste beziehen
127
        }
128
        else {
129
          //wenn Auge aus, dann an schalten
130
          PORTB |= (1<<PB2); // PinB2 auf 1 setzen
131
          //Zeitvariable auf Zufallswert für An-Zeit setzen          
132
          zeitAugenZweiSek = 10+ zufallsZeitWert();//Wert aus Zufallsliste beziehen
133
        }
134
      }
135
   /**/    
136
      //Auge 3: zweite Zeitvariable prüfen (Anzahl an Sekunden+1)
137
      if (zeitAugenDreiSek>10)
138
      {        
139
        zeitAugenDreiSek--; //Sekundären Zeitzähler reduzieren
140
      }
141
      else
142
      {
143
        //Wenn beide Zeitvariablen abgelaufen sind:
144
        //wenn Auge an, dann ausschalten
145
        if (PINB & (1<<PINB3)) //wenn PinB3 =1
146
        {
147
          //ausschalten
148
          PORTB &= ~(1<<PB3); // PinB3 auf 0 setzen
149
          //Zeitvariable auf Zufallswert für Aus-Zeit setzen          
150
          zeitAugenDreiSek = 10+ zufallsZeitWert(); //Wert aus Zufallsliste beziehen
151
        }
152
        else {
153
          //wenn Auge aus, dann anschalten
154
          PORTB |= (1<<PB3); // PinB3 auf 1 setzen
155
          //Zeitvariable auf Zufallswert für An-Zeit setzen          
156
          zeitAugenDreiSek = 10+ zufallsZeitWert(); //Wert aus Zufallsliste beziehen
157
          servoZyklus=0; //Servo aktivieren
158
          ZeitZaehlerServo=0;
159
        }
160
      }  
161
          
162
      
163
      //Auge 4: zweite Zeitvariable prüfen (Anzahl an Sekunden)
164
      if (zeitAugenVierSek>10)
165
      {        
166
        zeitAugenVierSek--; //Sekundären Zeitzähler reduzieren
167
      }
168
      else
169
      {
170
        //Wenn beide Zeitvariablen abgelaufen sind:
171
        //wenn Auge an, dann ausschalten
172
        if (PINB & (1<<PINB4)) //wenn PinB4 =1
173
        {
174
          //ausschalten          
175
          PORTB &= ~(1<<PB4); // PinB4 auf 0 setzen 
176
          //Zeitvariable auf Zufallswert für Aus-Zeit setzen
177
          zeitAugenVierSek = 10+ zufallsZeitWert();//Wert aus Zufallsliste beziehen
178
        }
179
        else {
180
          //wenn Auge aus, dann anschalten          
181
          PORTB |= (1<<PB4); // PinB4  auf 1 setzen 
182
          //Zeitvariable auf Zufallswert für An-Zeit setzen
183
          zeitAugenVierSek = 10+ zufallsZeitWert();//Wert aus Zufallsliste beziehen          
184
        }
185
      }
186
      /* */
187
    }
188
    
189

190
    //Servo-Code:
191
    if (servoZyklus <1) //Anzahl links-rechts-Zyklen des Servos
192
    {
193
    
194
      //Servo-Code
195
      if (ZeitZaehlerServo<59) //<1 Sek. (0,97s)
196
      {      
197
        // Servo nach links fahren        
198
        OCR0A = 148; //74;//37; //62;
199
      }
200
      if (ZeitZaehlerServo>58 && ZeitZaehlerServo<116)
201
      {
202
        // Servo nach rechts fahren
203
        OCR0A = 254;        
204
      }
205
      if (ZeitZaehlerServo>115) //Ein links-rechts-Zyklus durchlaufen
206
      {
207
        //Sekundenzähler zurücksetzen
208
        ZeitZaehlerServo=0;
209
        servoZyklus=servoZyklus+1;
210
      }      
211
    }
212
    
213
    }  //while (1)
214

215

216
  return 0;
217
}
218

219

220
 uint8_t zufallsZeitWert(){
221
   //gibt Zufallswert für Zeit zurück   
222
     if (laufZeit>49){laufZeit=0;}
223
     laufZeit++;
224
     return zeitWerteSekundaer[laufZeit-1];
225
}
Angehängte Dateien:
Moderator (Firma: Titel) Persönliche Seite #7571262
Lesenswert?

Bitte beachten, was über jeder Texteingabebox hier steht:

1
Antwort schreiben
2
Wichtige Regeln - erst lesen, dann posten!
3

4
  *  ...
5
  *  Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Mein Vorschlag: häng den Code an den nächsten Post einfach als ino Datei an und ich lösche den oben raus.

Kai W. schrieb:

Trotzdem habe ich den Verdacht, dass beim Takt/Teiler etc. ggf. etaws nicht stimmt.

Passt die clkdiv8 Fuse?

#7571569
Lesenswert?

Hallo Kai,

Kai W. schrieb:

const uint8_t zeitWerteSekundaer[50] = {1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2, 1, 2};

Die Daten für die Konstante werden in den RAM geschaufelt, lege sie in den Flash, dann hast du fast den gesamten RAM frei.

Schau mal hier rein:
https://www.mikrocontroller.net/articles/AVR-GCC-Tutorial#Flash_mit_PROGMEM_und_pgm_read

Etwas weiter unten gibt es auch noch die Beschreibung von "__flash", ist IMO einfacher in der Anwendung, nutze ich häufig für Konstanten.

Reinhard

OP #7571907
Lesenswert?

Vielen Dank für eure zahlreichen Antworten! Der Reihe nach:

Da ich nicht weiß, wie ich mit dem Microchip Studio ino-Dateien erzeuge, habe ich mal die main.c angehängt. Sorry, ich dachte, dass mein MiniProg nicht in die Kategorie "lang" fällt.

Bzgl. des "clkdiv8 Fuse": Ich habe im ersten Post ein Bild der Fuses angehängt - der LOW.CKDIV8 ist gesetzt oder was ist gemeint?

volatile: Alle im Interrupt und main verwendeten Variablen sind jetzt als "volatile" deklariert.

RAM/Flash: Vielen Dank für den Hinweis - ich habe versucht ihn umzusetzen. Wäre nett, wenn jemand meine Umsetzung kontrollieren würde.

Ich habe das in der main01.c angehängte Programm auf dem myC getestet und bin etwas ratlos, da:

  • die LEDs flackern nicht mehr, bleiben aber sehr lange an bzw. aus, z.T. gehen sie nur kurz an und direkt wieder aus (was ungefähr der vorgesehenen 1 Sek. entsprechen könnte).
  • Immerhin Durchläuft der Servo wie geplant einen Links-Rechts-Zyklus, wenn die LED 3 angeht.

MfG Kai

Angehängte Dateien:
#7571931
Lesenswert?

Kleinvieh macht auch Mist... warum nicht so? Ist kürzer und schneller.

uint8_t zufallsZeitWert(){ //gibt Zufallswert für Zeit zurück laufZeit++; if (laufZeit > 49) {laufZeit = 0;} return pgm_read_byte (zeitWerteSekundaer[laufZeit]);

Ansonsten scheint die Funktion des Programms ja deutlich näher am Soll zu liegen. Das deutet darauf hin, dass der RAM Verbrauch tatsächlich zu hoch war.

(Firma: L-E-A) #7572011
Lesenswert?

Kai W. schrieb:

Bzgl. des "clkdiv8 Fuse": Ich habe im ersten Post ein Bild der Fuses angehängt - der LOW.CKDIV8 ist gesetzt oder was ist gemeint?

Tja, wenn ich jetzt wüsste, was diese Oberfläche meint? Ich würde das so interpretieren, dass CKDIV8 aktiv ist, also auf 0 gesetzt wird. Damit wird der Takt der MCU, also auch der Takt, der deren Timer speist, durch 8 dividiert.

Grüßle, Volker

(Firma: L-E-A) #7572017
Lesenswert?

Steve van de Grens schrieb:

Volker B. schrieb:

Tja, wenn ich jetzt wüsste, was diese Oberfläche meint?

Sie meint die Fuses im default Zustand, also 1,2 MHz Taktfrequenz.

Die hexadezimale Darstellung unten drunter ist unmissverständlich.

...wenn man die ausgewählte MCU kennen würde...

Ah, ok, steht im Titel. Korrigiere:

...wenn man nicht zu faul wäre, das Datenblatt zu suchen... :-)

Grüßle, Volker

#7572039
Lesenswert?

Hallo,

geh nochmal einen großen Schritt zurück. Laut deiner Konfig nutzt du den internen 9,6MHz RC. Zusätzlich ist CKDIV8 Fuse aktiv. Das heißt dein µC taktet mit 1,2MHz.

Teste einmal ob die 2 Pins sauber takten? Ungetestet, kompiliert jedoch.

1
#define F_CPU 1200000UL  // 9,6MHz / 8
2
#include <avr/io.h>
3
#include <stdlib.h>
4
#include <avr/interrupt.h>
5
#include <avr/pgmspace.h>
6

7
ISR(TIM0_OVF_vect)      // 292 Hz ???
8
{
9
    PINB = _BV(PB1);    // Toggle
10
}
11

12
ISR(TIM0_COMPA_vect)
13
{
14
    PINB = _BV(PB2); 
15
}
16

17
int main(void)
18
{
19
    DDRB    = _BV(PB1); 
20
    DDRB   |= _BV(PB2); 
21
    TCCR0A  = _BV(WGM01) | _BV(WGM00);  // 8Bit Fast-PWM
22
    TIMSK0  = _BV(TOIE0);   // OVF
23
    TIMSK0 |= _BV(OCIE0A);  // CompareA
24
    OCR0A   = 127;
25
    TCCR0B  = _BV(CS01);    // Timer0 starten, Prescaler 8
26
    sei();   
27
         
28
    while (1) 
29
    { }
30
}
31

32
/*
33
  1 Takt = 833ns
34
  8*256 = 2048 Takte * 833ns = 1,706ms // ein OVF Aufruf
35
*/

Danach überlegst du dir nochmal wie du den Timer konfigurieren musst um vernünftig ein Servosignal zwischen 1-2ms zu erzeugen und dabei eine Periodendauer von 20ms einzuhalten. Kannst auch auf 10ms Periodendauer runtergehen. Das kommt der Auflösung zu Gute bei der Rechnerei.

OP #7573675
Lesenswert?

Vielen Dank für Eure Antworten!

Da nun wieder genug RAM zur Verfügung steht, habe ich den LED-Zustand mit vier bool-Variablen erfasst, anstatt ihn jedes mal abzufragen. Für meinen einfachen Fall genügt aber, denke ich, die einfache if-else-Konstruktion oder würdet ihr dringend zu der switch-case-Variante raten? Aber danke für den Hinweis auf den Zustandsautomaten, werde ich mir merken.

Die Optimierung von Georg G. an der zufallsZeitWert()-Funktion habe ich übernommen.

Danke für die Hinweise bzgl. CKDIV8 - das hatte ich nicht berücksichtigt. Ich habe Dein Programm, Veit D., am MCU getestet. Heraus kam eine Frequenz von 275 Hz (Signallänge an PB1 bzw. PB2: 1,820ms). Zugegebener Maßen, verstehe ich das Programm mit den "_BV()"-Ausdrücken nicht so ganz und hätte eigentlich eher unterschiedliche Frequenzen an den beiden Pins erwartet... Wie auf den Bildern zu sehen ist, trat auch eine wiederkehrende Störung auf... diese ist mir aber beim aktuellen Programm nicht mehr begegnet.

Nach ein wenig herumrechnen, habe ich mich dann für die folgende Variante entschieden: CKDIV8 deaktiviert (also 9,6 MHz) und den Timer-Prescaler auf 256. Das ergibt dann 1ms: 37 Takte (1,072ms gemessen), 2ms: 70 Takte (nachgemessen & korrigiert => 2,0ms) und einen Zyklus von 21,6ms(gemessen)

Servo und LEDs werden nun korrekt angesteuert und funktionieren wie erwartet... zumindest wenn ich nicht die zufallsZeitWert()-Funktion verwende. Wenn ich feste Werte vorgebe, werden diese korrekt umgesetzt. Die LEDs, die ihre Zeitwerte über die Funktion beziehen ändern ihren Zustand gar nicht oder nur sehr selten. Hat jemand eine Idee, voran das liegen könnte? Ich habe den aktualisierten Code angehängt.

MfG Kai

Angehängte Dateien:
#7573710
Lesenswert?

Hallo,

_BV() ist ein Makro. avr/sfr_defs.h> void _BV(bit_number); Andere Schreibweise statt Bit << schubsen.

Das beide Pins den gleichen Takt haben stimmt. Da hatte ich einen Denkfehler. Den OCRA Pin müßte der Timer selbst schalten damit die Pulsweite wie Anfangs gedacht verwendet wird. Dann hätte er den doppelten Takt. Ist jetzt auch egal.

Übrigens, das Oszi hat links eine Tastenreihe. Damit kann man im Bildschirm unten Meßwerte einblenden lassen. Zwecks deiner Frequenz und Duty Messung.

Mit Störungen meinst du sicherlich das "Rauschen" auf dem Low/High-Pegel? Das werden Störungen durch schlechte Kontakte/Masse sein. Steckbrett usw.

#7573858
Lesenswert?

Hallo,

habe mich mal rangemacht erstmal den Code aufzuräumen. Damit steigen die Chancen das jemand durchblickt. Und du auch um den Fehler zu finden, weil die Code Dopplung entfällt.

Dafür müßtest du das aber als Cpp Projekt neu anlegen und wenigstens unter Projekt > Properties >

etwas oberhalb > Configuration: > All Configuration

links Tooolchain:

AVR/GNU C++ Compiler >

Optimization: Level auf (-Os) setzen

C++ Miscellaneous: "other flags" auf -std=c++11 einstellen, Haken bei verbose kannste auch setzen

C kann leider nicht mit der Struktur "struct Auge" umgehen. Du kannst weiterhin C programmieren, abgesehen von der Struktur. Wenn das erstmal wie bisher funktioniert kann man weiter schauen ...

Angehängte Dateien:
OP #7574924
Lesenswert?

Vielen Dank für Eure Antworten und Hinweise! Und vielen Dank Veit D., dass Du Dir die Mühe gemacht hast, das Programm umzuschreiben. Dadurch hast Du für andere User sicherlich ein besseres Beispielprogramm erstellt, als mein Anfängerproggi. Zugegeben, mit struct kenne ich mich noch nicht aus, aber ich sehe ein, dass es in diesem Anwendungsfall wohl Sinn macht. Ich werde Eure zahlreichen Hinweise bei den nächsten Projekten versuchen zu berücksichtigen - vielen Dank dafür!

Nach der Ergänzung des fehlenden Adressoperators beim Zugriff auf das Array im Flash funktioniert das Programm nun wie erwartet, womit meine Frage beantwortet wäre. Ich hänge das endgültige Prog. noch an, falls noch anderen Anfängern die struct-Variante zu kompliziert ist.

Abschließend noch der Sinn des Programms: Wer mag, kann mit seinen Kindern zu Halloween Tischtennisbälle als Augen bemalen (Edding, Alufolie als Maske, Klarsichtfolie als Feuchteschutz), mit LED versehen und diese dann in einen kugeligen Busch im Vorgarten platzieren. Je ein Augenpaar wird dann zufällig an-/ausgeschaltet. Der Servo soll dann einen Ast schütteln.

Vielen Dank und viele Grüße Kai

Angehängte Dateien:

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren