Elektroonika komponente saab tellida Aliexpressist, hetkeseisuga Eestist kõiki neid osta ei saa. (Hinnad kõiguvad veidi ajas.)
Minul on olnud probleeme Yanwen Economic Air Mail postiteenusega, võimalusel vali muu.
2 tk ultraheli kaugusandureid US-015
(https://www.aliexpress.com/item/US-015-Ultrasonic-Senor-Module-Distance-... 1.16€x2) HC-SR04 - minu tellitud US-015 andurid ei töötanud korralikult, andsid eriti valesid näitusid, vahetasin HC-SR04 vastu välja ja esilagu olukord paranes. Aga tean, et vahel on ka HC-SR04 väga ebatöökindel. Igaks juhuks tasuks varuda mitu komplekti ultraheli kaugusandureid, soovitavalt eri sorti või silmnähtavalt erinevas kohas toodetud, et praagiks osutumisel saaks välja vahetada.
Patareikarp 4-le AA patareile
(https://www.aliexpress.com/item/1PCS-Plastic-Battery-Storage-Case-Box-Ho... 0.41€)
Patareikarp 6-le AA patareile (ühes kihis paigutatuna) 1tk või patareikarbid kolmele AA patareile (ühes kihis) 2tk - mootoritele on parem panna 6 alkaline AA patareid, sest 4-ga jääb nõrgaks ja mootorid vahel ei taha liikuma hakata.
Vajadusel USB 2.0 A-B juhe Arduino programmeerimiseks. Eespool näidatud lingilt Arduinot tellides sain ma kaasa nii lühikese juhtme, mis elu väga ebamugavaks teeb, hea oleks leida pikendus. Või - samasuguseid juhtmeid kasutavad tavaliselt printerid ja skännerid - laena sealt. Kui ei leia, siis telli.
(https://www.aliexpress.com/store/product/Supply-black-USB2-0-printer-lin... 2.65€)
Vanema vanuserühma roboti jaoks, mis erineva kõrgusega küünlaid kustutab:
Kahjus ei jõua ma sel aastal valmis sellist lihtsustatud robotit, mis eri kõrgustega hakkama saaks. Samuti ei ole mul sellele variandile koodinäidet pakkuda. Ja hilisema tõdemusena leidsin, et tõenäoliselt on vaja ühte väikest ja ühte suuremat servomootorit, kui mõlemad väikesed, siis arvatavasti ei jäksa ventilaatorit tõsta.
Tööriistad ja materjalid
Videotes näitan, kuidas ehitada robot valmis kõige vähemate tööriistade ja kõige kättesaadavamate materjalidega, mis ma välja mõelda oskasin. Kui Sul on olemas rohkem vahendeid, siis kasuta neid julgelt oma äranägemise järgi.
Minimaalselt vajalik:
Trell ja puuriotsikud (akutrelli asemel saab ka käsitsi opereeritavad puuri kasutada, sest läbida on vaja vaid paksu pappi)
Kuuma liimi püstol ja liimipulgad
Peene otsaga näpitsad
Juhtme lõikamiseks sobilikud tangid
Väiksemat sorti kruvikeeraja (moodulite kruviklemmidele sobib nii lapik kui ristpea)
Terav nuga (või muu tööriist juhtme koorimiseks)
Käärid
Kahepoolne teip - paks, pehme sort
Must isoleerpael
MacGyveri teip ehk duct tape
PVA liim
Joonlaud või paberile printimise võimalus
Pappi suuruses vähemalt 20x20cm, näiteks müsli karbid, aga mitte lainepapp vaid ühekihiline
AA patareisid 6tk (Alkaline tüüpi, 6 tk korraga, aga tegelikult kulub rohkem, sõltuvalt sellest kui palju sa robotit testid, soovitan igal juhul vahetult enne võistlust uued patareid panna)
9V patarei (kindlasti osta Alkaline märgistusega, teised saavad liiga ruttu tühjaks! Samuti võistluse jaoks uus patarei varuks.)
Välgumihkel - esialgseks testimiseks turvalisem kui küünlad, mis ümber võivad minna
Teeküünal hilisemaks testimiseks
Kasuks tuleb:
Jootekolb, jootetina traat kampoliga - mootoritele juhtmete külge jootmine tuleks töökindlusele kasuks.
Teine video: robotiplatvormi ehitus
Lisasin roboti kere papist juppide skeemid - lihtsalt sisseskännitud joonised. Link failile. Prindi välja nii, et ei tehta "fit to page" vaid suurus säilib. Kontrolli pildil oleva joonlaua järgi, kas sai õige suurus.
H-sillale juhtmete ühendamise skeem allpool pildina.
Kustuta Arduino IDEs valge ala täiesti tühjaks ja kopeeri järgnev joonte vahel olev tekst asemele, vaata, et algus ja lõpp ei saaks midagi ülearust ega kaotaks ühtki märki.
void setup() { //Siit algab see osa koodi, mis jookseb alguses üks kord.
//Ütleme oma ühendatud sisend/väljunditele, et nad hakaku väljunditeks.
pinMode(3, OUTPUT);//parem edasi
pinMode(5, OUTPUT);//parem tagasi
pinMode(6, OUTPUT);//vasak edasi
pinMode(9, OUTPUT);//vasak tagasi
//Lülitame nad kõik välja
analogWrite(3, 0);
analogWrite(5, 0);
analogWrite(6, 0);
analogWrite(9, 0);
delay(5000);//Enne liikuma hakkamist ootame 5 sekundit
}//Siin lõppeb see osa koodi, mis jookseb alguses üks kord.
void loop() {//Siin algab see osa koodi, mis jääb kordama.
//sõidame otse
analogWrite(3, 150);//sisend/väljund number 3 on parem edasi
analogWrite(6, 150);//sisend/väljund number 6 vasak edasi
// 150 eelmistel ridadel on number skaalalt 0-255,
// mis määrab, kas mootor lülitub täie võimsusega (255)
// või aeglasemale kiirusele sisse. Alla 150 võib juhtuda, et ei jäksa liikuda.
delay(1500);//Kui kaugele sõita jõuab, sõltub sellest ajavahemikust siin.
//peatume hetkeks
analogWrite(3, 0);//Number 0 lülitab mootori välja.
analogWrite(6, 0);
delay(500);
//Pöörame vasakule
analogWrite(3, 150);//sisend/väljund number 3 on parem edasi
analogWrite(9, 150);//sisend/väljund number 9 on vasak tagasi
delay(1000);//Kui palju pöörame, sõltub sellest ajavahemikust siin
//Peatume hetkeks
analogWrite(3, 0);
analogWrite(9, 0);
delay(500);
}//Siin lõppeb see osa koodi, mis jääb kordama.
Neljas video: Sensorid
Joonesensorite ühendused
Vasakpoolne sensor:
Sensori pin
Arduino / protoshiledi ühendus
VCC
5V (protoshiledi 5-ne pesa)
GND
GND (protoshiledi 5-ne pesa)
DO
7
AO
(jääb ühendamata)
Parempoolne sensor:
Sensori pin
Arduino / protoshiledi ühendus
VCC
5V (protoshiledi 5-ne pesa)
GND
GND (protoshiledi 5-ne pesa)
DO
8
AO
(jääb ühendamata)
Joonesensorite testimise kood
/**
* Joone andurite testimise kood. Saadab lugemi seeriamonitori.
*/
/*Defineerime muutujad millesse salvestame pinide numbrid, kuhu andurid ühendatud on.
Need aitavad inimesel koodi lugedes sellest palju kergemini aru saada.*/
int jooneandur_vasak = 7;
int jooneandur_parem = 8;
void setup() {
/*Määrame jooneandurite pinid sisenditeks.*/
pinMode(jooneandur_vasak, INPUT);
pinMode(jooneandur_parem, INPUT);
/*Paneme tööle arvutiga suhtluse seeriamonitori kaudu.*/
Serial.begin(9600);
}
void loop() {
/*Defineerime muutujad anduri hetkeväärtuse sisselugemiseks.*/
int joon_vasak;
int joon_parem;//Esialgu neisse pole väärtust salvestatud.
/*Loeme andurite seisu ja salvestame oma muutujasse.*/
joon_vasak = digitalRead(jooneandur_vasak);
joon_parem = digitalRead(jooneandur_parem);
/* digitalRead() on fuktsioon, mis loeb pini digitaalse väärtuse - annab
vastuseks kas 0 või 1.*/
/*Saadame salvestatud andurite lugemi seeriamonitori.*/
Serial.print("vasak: ");//Jutumärkides on string ehk tekst
Serial.print(joon_vasak);//ilma jutumärkideta on muutuja nimi, see asendub muutuja väärtusega
Serial.print(" parem: ");//print kirjutab ilma reavahetuseta
Serial.println(joon_parem);//println lisab lõppu reavahetuse
}
Kaugussensorite ühendused
Vasakpoolne (paremale vaatav) sensor
Sensori pin
Arduino / protoshiledi ühendus
VCC
5V (protoshiledi 5-ne pesa)
Trig
ühenduvad omavahel samale breadboardi reale
11
Echo
GND
GND (protoshiledi 5-ne pesa)
Parempoolne (vasakule vaatav) sensor
Sensori pin
Arduino / protoshiledi ühendus
VCC
5V (protoshiledi 5-ne pesa)
Trig
ühenduvad omavahel samale breadboardi reale
12
Echo
GND
GND (protoshiledi 5-ne pesa)
Kaugussensorite testimise kood
/**
* Kaugusandurite testimise kood. Saadab lugemi seeriamonitori.
*/
/*Kaugusandurid on erinevad näiteks joone anduritest selle poolest, et nad ei
edasta arduinole igal ajaheltkel pinge taset vastavalt sellele, kas näevad midagi või ei.
Vaid nad esiteks võtavad vastu käskluse, et nüüd hakaku mõõtma. Ja peale seda
tagastavad signaali, et nüüd tuli kaja tagasi. Arduino peab ise arvet pidama, kui
pikk aeg möödus sellest, kui ta andis käskluse mõõtma hakata kuni selleni, kuni
andus kaja saamisest märku andis. Selle ajavahemiku järgi ja heli levimise kiiruse
põhjal arvutatakse välja, kui kaugelt kaja tagasi tuli, mis ongi distants lähima objekini.
Aga selle keerulise protsessi on teised inimesed juba valmis programmeerinud ja
pakkinud selliseks ajsaks nagu library ehk teek. Library kasutamiseks tuleb see
1. installeerida (vt Sketch->Include libraries->Manage libraries. Otsi
NewPing nimeline ja installeeri see.
2. Leida File->Examples->[library nimi] alt näidiskood ja sealt vajalikud osad enda
koodi ümber kopeerida.
Esimene asi, mis tuleb kopeerida on library include rida:*/
#include <NewPing.h>
/*Teine asi, mis kopeerida, on libaray objekti loomise rida. Meil on 2 sensorit,
peame teisele tegema teise rea juurde. */
NewPing sonarParemale(11, 11, 200);//11 on Arduino pin, kuhu anduri ühendasime, 200 on max kaugus
NewPing sonarVasakule(12, 12, 200);//12 on Arduino pin -,,-
void setup() {
Serial.begin(9600);
}
void loop() {
Serial.print("Paremal on ruumi ");
Serial.print(sonarParemale.ping_cm());//sonarParemale.ping_cm() on siin funktsioon, mis tagastab
//mõõdetud kauguse numbri
Serial.print(" cm. \t");
delay(100);
Serial.print("Vasakul on ruumi ");
Serial.print(sonarVasakule.ping_cm());
Serial.println(" cm.");
delay(900);
}
Leegisensorite ühendused
Vasakpoolne sensor:
Sensori pin
Arduino / protoshiledi ühendus
VCC
5V (breadboardile laiendatud riba)
GND
GND (breadboardile laiendatud riba)
DO
(jääb ühendamata)
AO
A2
Keskmine sensor:
Sensori pin
Arduino / protoshiledi ühendus
VCC
5V (breadboardile laiendatud riba)
GND
GND (breadboardile laiendatud riba)
DO
(jääb ühendamata)
AO
A1
Parempoolne sensor:
Sensori pin
Arduino / protoshiledi ühendus
VCC
5V (breadboardile laiendatud riba)
GND
GND (breadboardile laiendatud riba)
DO
(jääb ühendamata)
AO
A0
Leegisensorite testimise kood
/**
* Leegi andurite testimise kood. Saadab lugemi seeriamonitori.
*/
/*Defineerime muutujad millesse salvestame pinide numbrid, kuhu andurid ühendatud on.*/
int leegiandur_vasak = A2;
int leegiandur_keskmine = A1;
int leegiandur_parem = A0;
void setup() {
/*Määrame leegiandurite pinid sisenditeks.*/
pinMode(leegiandur_vasak, INPUT);
pinMode(leegiandur_keskmine, INPUT);
pinMode(leegiandur_parem, INPUT);
/*Paneme tööle arvutiga suhtluse seeriamonitori kaudu.*/
Serial.begin(9600);
}
void loop() {
/*Defineerime muutujad anduri hetkeväärtuse sisselugemiseks.*/
int infrapuna_vasak;
int infrapuna_keskmine;
int infrapuna_parem;//Esialgu neisse pole väärtust salvestatud.
/*Loeme andurite seisu ja salvestame oma muutujasse.*/
infrapuna_vasak = analogRead(leegiandur_vasak);
infrapuna_keskmine = analogRead(leegiandur_keskmine);
infrapuna_parem = analogRead(leegiandur_parem);
/* analogRead() on fuktsioon, mis loeb analoog pinge väärtuse, ta annab vastuseks
mingi numbri vahemikus 0-1023. Seda fuktsiooni saab kasutada ainult A[x] pinidel.*/
/*Saadame salvestatud andurite lugemi seeriamonitori.*/
Serial.print("vasak: ");
Serial.print(infrapuna_vasak);
Serial.print("\t keskmine: "); // \t on "tab", lisab tühikud teatud tulbalaiuse saavutamiseni
Serial.print(infrapuna_keskmine);
Serial.print("\t parem: ");
Serial.print(infrapuna_parem);
Serial.print(" Kõige lähemal leegile on arvatavasti: ");
/*If ja else sellised "laused" programmeerimises, kus vastavalt tavalistes sulgudes
kirjeldatud tingimuse täidetusele tehakse kas üht või teist asja, mis on loogelistes
sulgudes if-i (tingimus tõene) või else-i (tingimus polnud tõene) järel.*/
if(infrapuna_vasak < infrapuna_parem && infrapuna_vasak < infrapuna_keskmine) {
/*Vasak andur näitab väiksemat numbrit kui parem ja väiksemat kui keskmine.
Meid huvitab kõige väiksem number, sest leegi läheduses läheb näit allapoole.*/
Serial.println(" vasak.");
}
else {
/*Vasak andur oli kas paremast või keskmisest siiski suurema numbriga*/
/*If-else lauseid saab ka üksteise sisse panna.*/
if(infrapuna_parem < infrapuna_vasak && infrapuna_parem < infrapuna_keskmine) {
/*Parem andur näitab nii vasakust kui keksmisest väiksemat numbrit.*/
Serial.println(" parem.");
}
else {
/*Kui eelmised võrdlused tõele ei vastanud, siis on järel vaid variant, et
keskmine andur näitab kõige väiksemat numbrit.*/
Serial.println(" keskmine.");
}
}
}
Viies video: Viimased jupid + Võistlusülesande kood
Oluline: Mis on vahepeal selgunud, et peaks olema teisiti
Selgus, et US-015 kaugusanduritest kõigepealt üks ja siis teine hakkasid andma mingit suvalist näitu. Olgu asjad kui kaugel tahes, nemad arvasid, et vastus on 5 sentimeetirt. Vahetasin nad välja HC-SR04 andurite vastu, mis veel ei ole jukerdama hakanud. Aga tõdemus nii varasemast kui ka seekordsest loost on, et ultraheli kaugusandurid on väga ettarvamatult ebatöökindlad. Hea oleks hankida neid rohkem ja hankida erinevaid, või siis HC-SR04, aga vaadata ringi, kas on värvi või logode järgi loota, et erineval ajal ja erinevas kohas toodetud. Et kui üks komplekt ostub praagiks, siis oleks millegi teisega võimalik proovida.
Kuigi selliste kollaste mootoritega robotikeredel, mida mitmelt poolt osta saab, on tavaliselt kaasas patareikarp 4-le AA patareile, siis selgub, et veidigi tühjenenud patareidega hakkavad mootorid tõrkuma. Mitte kogu aeg, aga mõnikord võivad mitte liikuma saada, kui neile vool peale antakse. Ja see on roboti puhul väga halb komme. Seega peaks kasutama 5-6 patareid. 4-se patareikarbi asemel tuleks hankida 6-ne või kaks tükki kolmeseid jadamisi ühendada. Siis on kindel, et mootorid ei kiilu kinni.
Patareikarp (või karbid) põhja all ei tahtnud ainult kahepoolse teibiga seal seista, vajusid lahti, panin sellele serva äärde kuuma liimi, sai korda.
Rataste paigutuse (roboti põhja kõrguse) ja põja all oleva patareikarbi mõõtmed on nii napikad, vahel õnnestus robotil rattad kohapeal pöörlema saada, sest patareikarp oli vastu maad ja kogu raskus ei olnud enam rataste peal. Mina panin katseks ratastele ümber kummirõngaid. Kui nad ära lendama ei kipuks, siis oleks päris head. Kummirõnga libeduse vastane omadus tuleb ka kasuks, lisaks sellele, et ta pool millimeetrit rattale läbimõõtu juurde annab. Püsivama lahendusena soovitaksin proovida jalgratta sisekummist lõigatud laiemat kummiriba. Või kui ma oleks varem teadnud, et keskkoht niipalju madalamale vajub, siis oleksin roboti põhja kleepinud eraldi osadest, kus mootorite kinnitused ühel tasapinnal ja patareikarbi kohal selles kihis auk. Ning too auk teise papikihiga ülevaltpoolt kaetud - see viiks patareikarbi kõrgemale rataste suhtes.
Lüliti mooduli ühendamine
Nimekirjas oleva lüliti mooduliga on selline lugu, et kuna minu kood hakkab kasutama interrupti, siis nupp vajab pull-up takistit, aga sellisel nupu moodulil on hoopis pull-down. Pole hullu, tegelik erinevus seisneb vaid selles, mis on nupu mooduli pinide juurde kirjutatud. Kui ühendame VCC ja GND omavahel ära vahetatuna, siis saabki pull-downist pull-up ja moodul töötab minu koodiga koostöös kenasti.
Ehk siis:
Lüliti moodul
VCC
GND breadboardil
OUT
Arduino pin 2
GND
5V breadboardil
Ventilaator ja mosfet transistori moodul
Mosfeti moodul
V+
Ventilaatori punane juhe
V-
Ventilaatori must juhe
Vin
Punane juhe, mis tuleb AA patareide karbist - ühenduse saab teha H-bridge kruviklemmi vahel, st sealt jätkata
GND
GND breadboardil
SIG
Arduino pin 4
VCC
5V breadboardil
GND
GND breadboardil
Ventilaator paiguta niiviisi, et ta sihiks mitte horisontaalselt vaid veidi allapoole, kui sinu robot on sama kõrge, nagu minu oma.
//Kaugusandurite library
#include <newping.h>
//Andurite Arduino ühendused
int leegiandur_vasak = A2;
int leegiandur_keskmine = A1;
int leegiandur_parem = A0;
int jooneandur_vasak = 7;
int jooneandur_parem = 8;
int sonarParemalePin = 11;
int sonarVasakulePin = 12;
//Mootorite Arduino ühendused
int parem_edasi = 3;
int parem_tagasi = 5;
int vasak_edasi = 6;
int vasak_tagasi = 9;
int ventilaator = 4;
//Arduino peal oleva led tulega anname märku, kui nupuvajutus oli edukas
int led = 13;
//Nupu ühendus
int start_stop_nupp = 2;
/*************************************************************************************************************************
SEADISTUSED - siin sekstioonis olevate muutujate väärtusi võib olla vajalik sul muuta
kas seetõttu, et sinu robot on natuke erinev minu omast või selleks, et targemini testida.
*************************************************************************************************************************/
/*Kui selle väärtuseks panna "true", siis USB juhet ühendatuna hoides prinditakse seeriamonitori
andurite lugemid ja roboti olek. Võistluse ajal aga pole seda vaja ja robot võiks kiiremini tegutseda ilma
tekste saatmata. Võistluse ajaks muuda see "true" ära "false"-iks.*/
bool debug = false;
/*Delay aeg millisekundites mille jooksul pöörates pwm kiirusega on pöörde tulemuseks 90 kraadine nurk.
Seda infot kasutatab kood robotit otse küünla suunas pööramise manöövri juures.*/
int yheksakymnekraadi_delay = 400;
/*Delay aeg, millega tehakse seintest ja väljaku äärejoonelt tagasipööramist. Kui tahad, et robot teeks 90
kraadise pöörde, siis pane see number sama, mis eelmine. Kui soovid teha laugema või tervama nurgaga tagasipööret,
siis muuda seda numbrit siin.*/
unsigned long poorde_aeg_ms = 400;
/*Mootorite kiirus (PWM väljund). Maksimum võimalik on 255. Kui kasutad 4 patareid, siis kindlasti 255.*/
int pwm = 200;
/*Kui pikaks ajaks ventilaator sisse lülitada kustutamiseks.*/
unsigned long kustutamise_aeg_ms = 4000;
/*Küünla ümber olevast rõngast välja taganemise manöövri ajaline pikkus, millisekundites.*/
unsigned long taganemise_aeg_ms = 600;
/*analogRead lugem infrapunaandurist, millest allapoole võime oletada, et andur on leegi läheduses*/
int oletatav_leek_infrapuna = 200;
/*Kaugus, mille mõõtmisel robot peaks pöörama takistusest (seinast) eemale.*/
int seinast_pooramise_kaugus = 27;
/*Kaugus mida andurid näitabvad siis, kui küünlast ollakse parajal kaugusel kustutamiseks.*/
int kyynlast_peatumise_kaugus = 19;
/*Peale nupu vajutamist roboti käivitamiseks ootame mingi aja enne kui see
tegelikult liikuma asub, et saaks käe rahulikult eemale võtta enne kui robot käest ära jookseb.*/
unsigned long stardi_viivitus_aeg_ms = 500;
/*************************************************************************************************************************
Seadistuste lõpp
*************************************************************************************************************************/
/*Kaugusandurite library objektid.*/
NewPing sonarParemale(sonarParemalePin, sonarParemalePin, 200);
NewPing sonarVasakule(sonarVasakulePin, sonarVasakulePin, 200);
/*Define laused et anda olekutele inimkeelsed nimed.*/
#define SEIS 0 //define lause lõpus ei käi semikoolonit
#define STARDI_VIIVITUS 1
#define OTSIMINE_OTSE 2
#define OTSIMINE_VASAKULE 3
#define OTSIMINE_PAREMALE 4
#define OTSEKS_KYYNLALE_KAUGEL 5
#define KYYNLASUUNAS 6
#define OTSEKS_KYYNLALE_LAHEDAL 7
#define KUSTUTAMINE 8
#define TAGANEMINE 9
/*Muutuja, mis hoiab infot millises "olekus" robot parasjagu töötab.*/
int olek = SEIS;
/*Muutujad, kuhu igal mõõtmisel salvestatakse andurite viimane lugem.*/
int infrapuna_vasak;
int infrapuna_keskmine;
int infrapuna_parem;
int joon_vasak;
int joon_parem;
int kaugus_vasakule;
int kaugus_paremale;
/*Muutujad, mis aitavad erinevate aja arvestustega.*/
unsigned long manoovri_algus_millis;
unsigned long nupuVonkeAeg = 0;
unsigned long viimaneNupuVonkeAeg = 0;
/******************************************************************************************************
* setup on see osa koodi, mida täidetakse üks kord Arduino kävitumisel.
* Siin on peamiselt pinide sisenditeks ja väljunditeks määramine.
*****************************************************************************************************/
void setup() {
pinMode(leegiandur_vasak, INPUT);
pinMode(leegiandur_keskmine, INPUT);
pinMode(leegiandur_parem, INPUT);
pinMode(jooneandur_vasak, INPUT);
pinMode(jooneandur_parem, INPUT);
/*Kuna kaugusandurid on seotud libraryga, siis nende pinmode toimub library koodis
ja meie ei pea siin sellega tegelema. */
pinMode(parem_edasi, OUTPUT);
pinMode(parem_tagasi, OUTPUT);
pinMode(vasak_edasi, OUTPUT);
pinMode(vasak_tagasi, OUTPUT);
pinMode(ventilaator, OUTPUT);
/*Igaks juhuks anname mootoritele ja ventilaatoritele käsu, et nad oleks välja lülitatud.*/
analogWrite(parem_edasi, 0);
analogWrite(parem_tagasi, 0);
analogWrite(vasak_edasi, 0);
analogWrite(vasak_tagasi, 0);
digitalWrite(ventilaator, LOW);
/*Interrupt on selline asi, mis võimaldab regeerida millelegi, näiteks nupuvajutusele, koheselt
sõltumata sellest, mida põhiline kood parasjagu teeb. Nupu interruti peale kutsutakse funktsioon
start_stop(), mis muudab muutuja "olek" väärtust, vaata koodifaili lõpuosa.*/
attachInterrupt(digitalPinToInterrupt(start_stop_nupp), start_stop, LOW);
/*Kui debugimine on sisse lülitatud, siis võimaldame seeriamonitori kaudu suhtlemise arvutiga.*/
if (debug == true) {
Serial.begin(9600);
}
} /*setup lõppeb. Liigutakse edasi loop-i juurde.*/
/******************************************************************************************************
* loop on see osa koodi, mis kordub lõputult.
* Roboti tegevused on siin sees, täidetakse üht osa koodist, vastavalt sellele, mis on
* muutuja "olek" väärtuseks (if ja if else laused)
*****************************************************************************************************/
void loop() {
/**
* Kõigepealt võtame kõigi andurite lugemid.
* Tähelepanu: infrapuna andurid annavad väiksema numbri kui leek on lähemal, st nt 40 leegi vastas ja 800 ilma leegita.
* Jooneandurid on 0 valgel põradal ja 1 mustal joonel.
*/
infrapuna_vasak = analogRead(leegiandur_vasak);
delay(2);/*Lühike paus enne järgmist analogRead käsklust teeb mõõtmistulemused töökindlamaks.*/
infrapuna_keskmine = analogRead(leegiandur_keskmine);
delay(2);
infrapuna_parem = analogRead(leegiandur_parem);
joon_vasak = digitalRead(jooneandur_vasak);
joon_parem = digitalRead(jooneandur_parem);
/*Kaugusanduritelt küsime kolm mõõtmist, viskame ära üksikud nullid ja võtame mõõtmiste keskmise. Seetõttu palju koodi siin.*/
int kaugused_vasakule[] = {0,0,0};
int kaugused_paremale[] = {0,0,0};
int kaugused_vasakule_summa = 0;
int kaugused_paremale_summa = 0;
int kaugused_vasakule_arv = 0;
int kaugused_paremale_arv = 0;
for (int i=0; i<3; i++) {
kaugused_vasakule[i] = sonarVasakule.ping_cm();
kaugused_paremale[i] = sonarParemale.ping_cm();
}
for (int i=0; i<3; i++) {
if (kaugused_vasakule[i]>0) {
kaugused_vasakule_summa += kaugused_vasakule[i];
kaugused_vasakule_arv++;
}
if (kaugused_paremale[i]>0) {
kaugused_paremale_summa += kaugused_paremale[i];
kaugused_paremale_arv++;
}
if (kaugused_vasakule_arv > 0) {
kaugus_vasakule = kaugused_vasakule_summa/kaugused_vasakule_arv;
}
else {
kaugus_vasakule = 0;
}
if (kaugused_paremale_arv > 0) {
kaugus_paremale = kaugused_paremale_summa/kaugused_paremale_arv;
}
else {
kaugus_paremale = 0;
}
}
/**
* Edasine tegevus sõltub sellest, millises olekus robot on. Täidetakse vaid üht järgenevatest
* if/ifelse lausetest, vastavalt sellele, mis väärtus on muutujal "olek".
*/
if (olek == SEIS) {
//seisvas olekus ei ole vaja robotil midagi teha, aga kui me just lülitusime seisvasse olekusse, siis
//mootorid ja ventilaator võisid töötada ja need tuleb välja lülitada, seetõttu need käsud siin
analogWrite(parem_edasi, 0);
analogWrite(parem_tagasi, 0);
analogWrite(vasak_edasi, 0);
analogWrite(vasak_tagasi, 0);
digitalWrite(ventilaator, LOW);
digitalWrite(led, LOW);
}
else if (olek == STARDI_VIIVITUS) {
//Selles olekus ei tehta mitte midagi, ainult LED pannakse põlema, et inimene näeks, et nupuvajutus oli edukas.
//Led jääb põlema kõigis olekutes peale SEIS oleku.
digitalWrite(led, HIGH);
if (millis() >= manoovri_algus_millis + stardi_viivitus_aeg_ms) {
//Kui stardi viivituse aeg on täis tiksunud, siis minnakse otsimise olekusse.
olek = OTSIMINE_OTSE; //Läheme otsimise olekusse.
}
}
else if (olek == OTSIMINE_OTSE) {
analogWrite(parem_edasi, pwm);
analogWrite(parem_tagasi, 0);
analogWrite(vasak_edasi, pwm);
analogWrite(vasak_tagasi, 0);
/*Vaatame, ega me musta joone peale ei ole sõitnud. Otsimise olekus võib küünalde rõngastelt tagasi
pöörata sama moodi nagu väljaku servast.
Kui hakkame, siis pöörame joonelt eemale ca 90 kraadi.*/
if (joon_vasak == 1) {
olek = OTSIMINE_VASAKULE;
manoovri_algus_millis = millis();
}
else if (joon_parem == 1) {
olek = OTSIMINE_PAREMALE;
manoovri_algus_millis = millis();
}
/*Kui joont ei leitud, siis järgmiseks vaatame, ega me seinale otsa sõitma ei hakka.
Kui hakkame siis pöörame seinast eemale ca 90 kraadi.*/
else if (kaugus_vasakule > 0 && kaugus_vasakule < seinast_pooramise_kaugus) {
//Kaugus 0 tähendab, et midagi ei nähta, seega pööramiseks peab kaugus olema nulli ja "lähedal" vahel.
olek = OTSIMINE_PAREMALE;
manoovri_algus_millis = millis();
}
else if (kaugus_paremale > 0 && kaugus_paremale < seinast_pooramise_kaugus) {
olek = OTSIMINE_VASAKULE;
manoovri_algus_millis = millis();
}
/*Kui kumbagi - joont ega seina ees ei olnud, siis vaatame,
kas mõni leegiandur näeb nii palju infrapuna, et võiks oletada küünalt selles suunas.
Kui näeb, siis proovime pöörata täpselt maksimaalse infrapuna suunas.*/
else if (infrapuna_vasak < oletatav_leek_infrapuna || infrapuna_parem < oletatav_leek_infrapuna || infrapuna_keskmine < oletatav_leek_infrapuna) {
olek = OTSEKS_KYYNLALE_KAUGEL;
}
}
else if (olek == OTSIMINE_VASAKULE) {
/*Mootorid pööravad, kuni aeg pole täis tiksunud.*/
analogWrite(parem_edasi, pwm);
analogWrite(parem_tagasi, 0);
analogWrite(vasak_edasi, 0);
analogWrite(vasak_tagasi, pwm);
/*Kontrollime ainult leegisensoreid, sest joonele ega seinale ümber enda pöörates peale ei sõida.*/
if (infrapuna_vasak < oletatav_leek_infrapuna || infrapuna_parem < oletatav_leek_infrapuna || infrapuna_keskmine < oletatav_leek_infrapuna) {
olek = OTSEKS_KYYNLALE_KAUGEL;
}
if (millis() >= manoovri_algus_millis + poorde_aeg_ms) {
//Kui aeg on täis tiksunud:
olek = OTSIMINE_OTSE; //Kui pööre sooritatud, jätkame otsimise otse sõitu.
}
}
else if (olek == OTSIMINE_PAREMALE) {
analogWrite(parem_edasi, 0);
analogWrite(parem_tagasi, pwm);
analogWrite(vasak_edasi, pwm);
analogWrite(vasak_tagasi, 0);
/*Kontrollime ainult leegisensoreid, sest joonele ega seinale ümber enda pöörates peale ei sõida.*/
if (infrapuna_vasak < oletatav_leek_infrapuna || infrapuna_parem < oletatav_leek_infrapuna || infrapuna_keskmine < oletatav_leek_infrapuna) {
olek = OTSEKS_KYYNLALE_KAUGEL;
}
if (millis() >= manoovri_algus_millis + poorde_aeg_ms) {
//Kui aeg on täis tiksunud:
olek = OTSIMINE_OTSE; //Kui pööre sooritatud, jätkame otsimise otse sõitu.
}
}
else if (olek == OTSEKS_KYYNLALE_KAUGEL || olek == OTSEKS_KYYNLALE_LAHEDAL) {
/*Vaatame, millises anduris oli kõige rohkem infrapuna. Pöörame roboti sellest andurist u 30 kraadi vasakule.
Teeme 10 mõõtmist u 6 kraadiste vahedega, et kaardistada infrapuna selles sektoris.
Siis leiame maksimaalse näidu nende 10 hulgas ja keerame end täpselt selle suunas.*/
if (olek == OTSEKS_KYYNLALE_KAUGEL && infrapuna_vasak < infrapuna_keskmine && infrapuna_vasak < infrapuna_parem) {
//Pöörame 75 kraadi vasakule, et sealt mõõta 60 kraadine sektor, mille keskkoht on 45 kraadi vasakul praegusest seisust.
analogWrite(vasak_edasi, 0);
analogWrite(vasak_tagasi, pwm);
analogWrite(parem_edasi, pwm);
analogWrite(parem_tagasi, 0);
delay(75.0/90.0 * yheksakymnekraadi_delay);
}
else if (olek == OTSEKS_KYYNLALE_KAUGEL && infrapuna_parem < infrapuna_keskmine && infrapuna_parem < infrapuna_vasak) {
//Pöörame 15 kraadi paremale, et sealt mõõta 60 kraadine sektor, mille keskkoht on 45 kraadi paremal praegusest seisust.
analogWrite(vasak_edasi, pwm);
analogWrite(vasak_tagasi, 0);
analogWrite(parem_edasi, 0);
analogWrite(parem_tagasi, pwm);
delay(15.0/90.0 * yheksakymnekraadi_delay);
}
else {//Olek on OTSEKS_KYYNLALE_LAHEDAL või siis olek on -KAUGEL, aga keskmine sensor näeb kõige rohkem infrapuna
//Pöörame 30 kraadi vasakule, et sealt mõõta 60 kraadine sektor, mille keskkoht on preagune positsioon.
analogWrite(vasak_edasi, 0);
analogWrite(vasak_tagasi, pwm);
analogWrite(parem_edasi, pwm);
analogWrite(parem_tagasi, 0);
delay(30.0/90.0 * yheksakymnekraadi_delay);
}
//Peatame mootorid
analogWrite(vasak_edasi, 0);
analogWrite(vasak_tagasi, 0);
analogWrite(parem_edasi, 0);
analogWrite(parem_tagasi, 0);
//Teeme 11 mõõtmist u 60 kraadises sektoris.
int infrapuna_sektoris[11];
//Käivitame mootrid
analogWrite(vasak_edasi, pwm);
analogWrite(vasak_tagasi, 0);
analogWrite(parem_edasi, 0);
analogWrite(parem_tagasi, pwm);
//Jagame perioodi osadeks ja teeme iga osa järel mõõtmise keskmise sensoriga
for (int i=0; i<11; i++) {
if (i>0) {
delay(6.0/90.0 * yheksakymnekraadi_delay);
}
infrapuna_sektoris[i] = analogRead(leegiandur_keskmine);
}
//Peatame mootorid
analogWrite(vasak_edasi, 0);
analogWrite(vasak_tagasi, 0);
analogWrite(parem_edasi, 0);
analogWrite(parem_tagasi, 0);
//Leiame kõrgeima infrapunaga sektori indeksi - anduri näit on sel juhul madalaim number.
int madalaim_nait = 1023;//1023 on maksimum, mida analogRead anda saab, reaalsed näidud on sellest madalamad.
int madalaima_indeks = 0;
for (int i=0; i<11; i++) {
if (infrapuna_sektoris[i] < madalaim_nait) {
madalaim_nait = infrapuna_sektoris[i];
madalaima_indeks = i;
}
}
if (debug == true) {//Kui tegeleme testimisega, siis saadame andmed arvutisse
Serial.println();
Serial.print("Infrapuna mõõtmised: \t");
for (int i=0; i<11; i++) {
Serial.print(infrapuna_sektoris[i]);
Serial.print("\t");
}
Serial.print("\t\tLeek on sektoris: ");
Serial.println(madalaima_indeks);
Serial.println();
delay(5000); //Võtame 5 sek aega, et jõuaks seeriamonitoris lugeda juttu.
}
delay(500);//Ootame eelmise pöördliikumise inertsi möödumist
//Pöörame tagasi selle sektori suunas.
analogWrite(vasak_edasi, 0);
analogWrite(vasak_tagasi, pwm);
analogWrite(parem_edasi, pwm);
analogWrite(parem_tagasi, 0);
delay((11-madalaima_indeks)*6.0/90.0 * yheksakymnekraadi_delay);
analogWrite(vasak_edasi, 0);
analogWrite(vasak_tagasi, 0);
analogWrite(parem_edasi, 0);
analogWrite(parem_tagasi, 0);
if (olek == OTSEKS_KYYNLALE_KAUGEL) {
olek = KYYNLASUUNAS;
}
else {
olek = KUSTUTAMINE;
manoovri_algus_millis = millis();
}
}
else if (olek == KYYNLASUUNAS) {
analogWrite(parem_edasi, pwm);
analogWrite(parem_tagasi, 0);
analogWrite(vasak_edasi, pwm);
analogWrite(vasak_tagasi, 0);
//Kontrollime kas näeme ees leeki, kui ei näe leeki, aga näeme takistust või joont, siis oleme eksiteel arvamusega, et liigume küünla suunas ja
//peame minema tagasi otsimise olekusse.
if (
infrapuna_keskmine > oletatav_leek_infrapuna &&
(
kaugus_vasakule <= seinast_pooramise_kaugus || kaugus_paremale <= seinast_pooramise_kaugus ||
joon_vasak == 1 || joon_parem == 1
)
) {
olek = OTSIMINE_OTSE;
}
//Kui leek on silmapiiril, siis jooni ignoreerime ja kaugusandurist vaatame, millal on paras koht küünla ees peatuda.
else if ((kaugus_vasakule > 0 && kaugus_vasakule <= kyynlast_peatumise_kaugus) || (kaugus_paremale > 0 && kaugus_paremale <= kyynlast_peatumise_kaugus)) {
//Kaugus 0 tähendab, et midagi ei nähta, seega otsustamiseks peab kaugus olema nulli ja "lähedal" vahel.
olek = OTSEKS_KYYNLALE_LAHEDAL;
}
}
else if (olek == KUSTUTAMINE) {
//Kustustamise olekus lülitatakse ventilaator tööle ja sellest olekust väljutakse siis
//kui ettenähtud aeg on möödas.
//Kuni tegevuse aeg pole täis tiksunud kordame siin käsklust mootorid välja ja ventilaator tööle lülitada.
analogWrite(parem_edasi, 0);
analogWrite(parem_tagasi, 0);
analogWrite(vasak_edasi, 0);
analogWrite(vasak_tagasi, 0);
digitalWrite(ventilaator, HIGH);
if (millis() >= manoovri_algus_millis + kustutamise_aeg_ms) {
//Kui aeg on täis tiksunud:
olek = TAGANEMINE; //Järgmine tegevus on taganemine.
manoovri_algus_millis = millis(); //Salvestame ka selle tegevuse algus aja süsteemi kella millisekundites.
digitalWrite(ventilaator, LOW);
}
}
else if (olek == TAGANEMINE) {
//Taganemise olekus lülitatakse mootorid tagurpidi käima ja sellest olekust väljutakse siis
//kui ettenähtud aeg selle manöövri sooritamiseks on möödas.
//Kuni manöövri aeg pole täis tiksunud kordame siin käsklust mootorid tagurpidikäigule lülitada.
analogWrite(parem_edasi, 0);
analogWrite(parem_tagasi, pwm);
analogWrite(vasak_edasi, 0);
analogWrite(vasak_tagasi, pwm);
if (millis() >= manoovri_algus_millis + taganemise_aeg_ms) {
//Kui aeg on täis tiksunud:
olek = OTSIMINE_PAREMALE; //Järgmine tegevus on otsimine ja loogiline on teha mingi pööre.
}
}
/*Kui debugimine seeriamonitoris on sisse lülitatud, siis trükitakse sinna roboti olek ja andurite lugemid.*/
if (debug == true) {
Serial.print("olek:");
if (olek == SEIS) { Serial.print("SEIS"); }
else if (olek == STARDI_VIIVITUS) { Serial.print("STARDI_VIIVITUS"); }
else if (olek == OTSIMINE_OTSE) { Serial.print("OTSIMINE_OTSE"); }
else if (olek == OTSIMINE_PAREMALE) { Serial.print("OTSIMINE_PAREMALE"); }
else if (olek == OTSIMINE_VASAKULE) { Serial.print("OTSIMINE_VASAKULE"); }
else if (olek == OTSEKS_KYYNLALE_KAUGEL) { Serial.print("OTSEKS_KYYNLALE_KAUGEL"); }
else if (olek == KYYNLASUUNAS) { Serial.print("KYYNLASUUNAS"); }
else if (olek == OTSEKS_KYYNLALE_LAHEDAL) { Serial.print("OTSEKS_KYYNLALE_LAHEDAL"); }
else if (olek == KUSTUTAMINE) { Serial.print("KUSTUTAMINE"); }
else if (olek == TAGANEMINE) { Serial.print("TAGANEMINE"); }
}
Serial.print("\tleegi: ");
Serial.print(infrapuna_vasak);
Serial.print("\t");
Serial.print(infrapuna_keskmine);
Serial.print("\t");
Serial.print(infrapuna_parem);
Serial.print( "\tkaugus: ");
Serial.print(kaugus_vasakule);
Serial.print("\t");
Serial.print(kaugus_paremale);
Serial.print( "\tjoone: ");
Serial.print(joon_vasak);
Serial.print("\t");
Serial.println(joon_parem);
} /*loop lõppeb. Minnakse tagasi loopi algusesse.*/
/**************************************************************************************************/
/**
* See funktsioon kutsutakse, kui tuvastati nupu oleku muutus (interrupt).
* Aga kuna nupp tavaliselt ei ühendu perfektselt
* vaid annab võnkumistega signaali, siis siin tehakse veidi ajaarvestust, et mitmest järjestikusest
* võnkest lugeda kokku üks tegelik nupuvajutus.
*
* Kui nupuvajutus toimus, siis muudetakse roboti olekut:
* - seisvast olekust küünla otsimise olekusse (stardi viivitusega),
* - ükskõik millisest teisest (tegutsemise) olekust seisvasse olekusse.
*/
void start_stop() {
nupuVonkeAeg = millis();
if (nupuVonkeAeg - viimaneNupuVonkeAeg > 330) {
if (olek == SEIS) {
olek = STARDI_VIIVITUS;
manoovri_algus_millis = millis();
}
else {
olek = SEIS;
}
}
viimaneNupuVonkeAeg = nupuVonkeAeg;
}