LightBot – ATtiny Roboter
Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /home/www/nicomania_de/wp-content/plugins/wp-syntax/wp-syntax.php on line 380
Auch wenn man schon groß und erwachsen ist, so reizt es dann doch den Mann, mal ein kleines Spielzeug zu bauen, sei es auch noch so sinnfrei. Deshalb habe ich einen kleinen und einfachen Roboter bauen wollen, der dem Licht hinterher jagt. Dazu braucht es einen Mikrocontroller und zwei Lichtsensoren, damit die Richtung des Lichts erkannt werden kann. Den Antrieb hatte ich ursprünglich mit zwei Vibrationsmotoren geplant, da mich diverse YouTube Videos inspiriert hatten, in denen ein „Brushbot“ mit einem Vibrationsmotor auf einem Zahnbürstenkopf durch die Gegend flitzt. Das Prinzip funktioniert recht gut, aber eine brauchbare Richtungssteuerung mit zwei Motoren links und rechts war nicht drin. Ich habe da viel experimentiert und Zeit verbraten, hohe und niedrige Spannung, Geschwindigkeit und verschiedene Montageorte. Vibrationsmotoren waren wohl für meinen Zweck nicht brauchbar. Deshalb bin ich zum altbewährten Fortbewegungsmittel zurückgekehrt: dem Rad. Schnell ein altes Spielzeugauto geschlachtet und auf die Wellen der Vibrationsmotoren geklebt. Damit war die Richtungssteuerung natürlich ein Traum! Schnell, effizient und präzise, auch wenns nicht ganz so lustig ist, wie ein vibrierend hüpfender Roboter 🙂
Die Hardware ist recht übersichtlich. Neben dem Mikrocontroller gibt es da einen Akku (Li-Po), zwei mini Elektromotoren, zwei LDR (lichtempfindliche Widerstände), zwei Transistoren, eine LED, ein Schalter und drei Widerstände. Das alles wollte ich auch so klein wie möglich unterbringen. Deshalb habe ich mir eine vollflächige Kupferplatine genommen und die Leiterbahnen frei mit einem Dremel ausgefräst. In meinem routing komme ich dabei mit einer Seite und zwei Durchkontaktierungen für die Motoren aus.
Der Mikrocontroller hat einen Spannungsbereich von 2,7 bis 5,5V, weshalb ich einen LiPo Akku aus dem Modellbau verwendet habe. Ich glaube, ich hatte ihn mal aus einem kleinen Helimodell ausgeschlachtet, der war so schön klein mit seinen 140mAh. Ein LiPo pendelt mit seiner Spannung zwischen 3,0 und 4,2V, deshalb brauchen wir keinerlei Spannungsregler und können direkt die Motoren und den Controller befeuern. Zum laden benutze ich entweder einen Modellbaulader oder einen USB Ladestecker. An der Unterseite habe ich dazu einen kleinen Stecker vorgesehen, denn der Akku ist festgeklebt und angelötet.
Der ATtiny kann pro Ausgangspin nur 20mA schalten, die Motoren ziehen pro Stück aber 50 bis 60mA, weshalb ich noch NPN Transistoren davor schalten musste. Ich habe die kleine BC849 Variante genommen, die gibt es als SMD Sot23 Baugröße. Wenn du alle Bauteile wie ich in SMD Größe benutzt, erfordert das Löten ein wenig Fingerspitzengefühl und eine Pinzette.
Als Lichtsensoren habe ich zwei LDRs verwendet. Das sind Lichtempfindliche Widerstände, die im Dunkeln ca. 500 kOhm und im Hellen ca. 0 Ohm haben. Zusammen mit einem einfachen Spannungsteiler mit zwei Festwiderständen von 10 kOhm ergibt das dann im Dunklen 0,5V und im Hellen 5V (VCC), die wir mit dem AD Wandler des ATtiny messen können (Ausgegangen von 5V Versorgungsspannung). Damit das Sichtfeld der einzelnen Sensoren nicht zu breit ist, habe ich ein Stück schwarzen Schrumpfschlauch darum geschrumpft. Wenn also der eine LDR eine höhere Helligkeit wahrnimmt, ist der zweite davon nicht beeinflusst und man kann einen eindeutigen Helligkeitsunterschied zwischen links und rechts feststellen.
An einem Pin habe ich noch eine einzelne LED angeschlossen, damit ich diverse Statusmeldungen ausgeben kann. In meinem Code blinkt sie z.B. nach jedem Messdurchlauf drei Mal kurz hintereinander. Ich spiele aber schon mit dem Gedanken, diesen Pin einer IR Diode zu opfern, um evtl. die zweite Version des LightBot mit einer Infrarot-Fernbedienung zu steuern.
Hier findest du den Code für den ATtiny. Manches könnte man bestimmt anders lösen oder eleganter programmieren. Ich bin kein Informatiker und absolut offen für Vorschläge. Beispielsweise habe ich die Ansteuerung für die Motoren zwar mit PWM (Pulsweitenmodulation) erledigt, jedoch in Software. Man kann das auch in Hardware machen, da der ATtiny zwei Ausgänge dafür besitzt. Das hätte den Vorteil, dass er gleichzeitig die Helligkeit messen und sich bewegen kann. Ich habe den Code so gut es geht kommentiert, damit es nachvollziehbar bleibt. Dennoch setze ich ein gewisses Maß an Programmierkenntnisse in C voraus, denn ich kann hier leider keinen Programmierkurs abhalten. Die Bewegungen und Messungen habe ich in Funktionen ausgelagert, damit ich sie in der main nur kurz aufrufen muss, um mich rein auf die Steuerung konzentrieren zu können. Wenn du das Projekt nur nachbauen möchtest, findest du in der ZIP Datei auch die fertigen Hex Dateien zum direkten flashen für den ATtiny 13, 25 und 85. Diese Typen sind Pinkompatibel und reichen vom Speicherplatz aus.
Hier der Programmcode für den ATtiny:
[showhide more_text=“>> Hier klicken um Code anzuzeigen“ less_text=“<< Hier klicken um Code auszublenden“]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | // Lightbot V1.1 ATtiny 13/25/85 // ************** Includes ************************************************** #include <avr/io.h> #include <util/delay.h> #define F_CPU 20000000UL //Takt in Hz // ************** Einstellungen ********************************************* #define toleranz 30 // Toleranz #define tempoL 200 // PWM Tempo Motor links (0-255) #define tempoR 200 // PWM Tempo Motor rechts (0-255) #define tempoV 180 // PWM Tempo Motoren vor (0-255) #define impulsL 18 // Impulsdauer Motor links (in ms) #define impulsR 18 // Impulsdauer Motor rechts (in ms) #define impulsV 15 // Impulsdauer Motoren vor (in ms) #define ul 3 // U-Turn Impulse links #define ur 4 // U-Turn Impulse rechts #define i360 9 // Anzahl Schritte für u-turn // ************** Variablen ************************************************* volatile int i,k,z; // Zaehler und Zwischenspeicher // ************** Function: Licht (0-1023) ********************************** int MESSWERT (int kanal){ int a=5, f=a, j=a; // a>0 Anzahl der Messzyklen long int wert=0, wert1=0, wert2=0 ; ADCSRA=0x80; // ADC eingeschaltet, kein Prescale ADMUX=kanal; // Pin auswaehlen ADCSRA |=_BV(ADSC); // Dummymessung starten while (ADCSRA & (1<<ADSC)) {;} // Dummymessung abwarten while(j){ while(f){ ADCSRA |=_BV(ADSC); // Messung starten while (ADCSRA & (1<<ADSC)) {;} // auf Abschluss der Konvertierung warten wert+=ADCW; // Wert uebergeben f--; } wert1 = wert/a; // Mittelwert bilden wert2 += wert1; j--; } wert=(wert2/a); return (wert); } int lichtL(void) { return (1023-MESSWERT(2)); // invertieren } int lichtR(void) { return (1023-MESSWERT(3)); // invertieren } // ************** Function: Bewegung **************************************** void vor(void) { k=255-tempoV; for(i=impulsV;i>0;i--) { PORTB |= 0b000011; for (z=0; z<tempoV; z++); PORTB &= 0b111100; for (z=0; z<k; z++); } _delay_ms(100); } void links(void) { k=255-tempoL; for(i=impulsL;i>0;i--) { PORTB |= 0b000010; for (z=0; z<tempoL; z++); PORTB &= 0b111101; for (z=0; z<k; z++); } _delay_ms(100); } void rechts(void) { k=255-tempoR; for(i=impulsR;i>0;i--) { PORTB |= 0b000001; for (z=0; z<tempoR; z++); PORTB &= 0b111110; for (z=0; z<k; z++); } _delay_ms(100); } void turn(void) { if (lichtL()>lichtR()) { for(int t = 0; t<=ul ; t++) { links(); _delay_ms(10); } } else{ for(int t = 0; t<=ur ; t++) { rechts(); _delay_ms(10); } } } void ini(void) { int pos=0; int maxSense=0; for(int curpos=0;curpos<i360;curpos++) { int iLicht = lichtL(); if(iLicht>maxSense) { maxSense=iLicht; pos=curpos; } rechts(); _delay_ms(100); } while(pos>0) { rechts(); pos--; _delay_ms(100); } } // ************** Function: LED (an, aus oder Angabe in ms) ***************** void led(int l) { if (l==1) // LED an { PORTB &= 0b111011; //low } else if (l==0) // LED aus { PORTB |= 0b000100; // high } else // LED blitz { PORTB &= 0b111011; //low for (i=l/10; i>0; i--) _delay_ms(10); PORTB |= 0b000100; // high } } // ************** MAIN ****************************************************** int main(void) { // ########## config ############# // 5 4 3 2 1 0 // [X LDR1 LDR2 LED MB MA] // [X E E A A A] DDRB = 0b000111; // Ausgang=1, Eingang=0 PORTB = 0b000100; // preset led(1000); _delay_ms(250); // ########## bewegen ############ ini(); while(1) { if(lichtL() > lichtR() + toleranz) // wenn links heller { links(); // fahre links } else if(lichtR() > lichtL() + toleranz) // wenn rechts heller { rechts(); // fahre rechts }else { // blinken led(10); _delay_ms(100); led(10); _delay_ms(100); led(10); } _delay_ms(80); } return 1; } |
[/showhide]
Download Programmcode und Hex Dateien
So, das sollte an Info schon reichen, um das Spielzeug nachzubauen, zu verbessern oder weiter zu entwickeln. Lass dich noch von meinem Demovideo inspirieren. Viel Spaß beim Spielen 🙂
Noch keine Kommentare