This shows you the differences between two versions of the page.
pm:prj2023:iotelea:bionic_hand [2023/05/28 22:45] constantin.cinjau [Software Design] |
pm:prj2023:iotelea:bionic_hand [2023/05/29 19:09] (current) constantin.cinjau [Rezultate obtinute] |
||
---|---|---|---|
Line 43: | Line 43: | ||
* Un **checkbox** care este bifat la un interval de o secunda, pentru ca utilizatorul sa isi dea seama ca telefonul are o **conexiune stabila** cu placuta prin intermediul bluetooth-ului si primeste date constant de la aceasta. | * Un **checkbox** care este bifat la un interval de o secunda, pentru ca utilizatorul sa isi dea seama ca telefonul are o **conexiune stabila** cu placuta prin intermediul bluetooth-ului si primeste date constant de la aceasta. | ||
* Un buton prin intermediul caruia poate face transmisia manual(in mod normal aceasta ar trebui sa se declanseze de fiecare data cand unul dintre slidere este actionat, dar m-am gandit ca ar fi util ca utilizatorul sa poata trimita si el manual in cazul in care i se pare ca nu s-a transmis ce a vrut). | * Un buton prin intermediul caruia poate face transmisia manual(in mod normal aceasta ar trebui sa se declanseze de fiecare data cand unul dintre slidere este actionat, dar m-am gandit ca ar fi util ca utilizatorul sa poata trimita si el manual in cazul in care i se pare ca nu s-a transmis ce a vrut). | ||
- | O imagine cu interfata aplicatiei: | + | |
+ | {{https://ocw.cs.pub.ro/courses/_media/pm/prj2023/iotelea/bionic_hand_app.jpg?300x500}} | ||
Pentru comunicarea realizata prin Bluetooth, a trebuit sa folosesc un fel de "protocol" de comunicatie, in sensul ca aplicatia de pe telefon am definit-o astfel incat atunci cand doreste sa trimita un mesaj(cand se schimba pozitia unui slider sau se apasa butonul), marcheaza acest lucru prin trimiterea caracterului '?', pentru a delimita 2 valori diferite, se foloseste caracterul '&', iar pentru a marca finalul unui mesaj se foloseste caracterul ';', deci mesajul pe care il primim pe placuta va fi in formatul: ?fingers=value1&wrist=value2;. | Pentru comunicarea realizata prin Bluetooth, a trebuit sa folosesc un fel de "protocol" de comunicatie, in sensul ca aplicatia de pe telefon am definit-o astfel incat atunci cand doreste sa trimita un mesaj(cand se schimba pozitia unui slider sau se apasa butonul), marcheaza acest lucru prin trimiterea caracterului '?', pentru a delimita 2 valori diferite, se foloseste caracterul '&', iar pentru a marca finalul unui mesaj se foloseste caracterul ';', deci mesajul pe care il primim pe placuta va fi in formatul: ?fingers=value1&wrist=value2;. | ||
Pentru a comunica cu **modulul Bluetooth HC-05** am folosit biblioteca SoftwareSerial.h prin intermediul careia am creat un obiect de tipul SoftwareSerial pe care am atasat pinii: 9(pentru RX) si 8(pentru TX). Din cadrul acestei clase am utilizat 3 functii importante: available(), write() si read(). Aceste functii le-am folosit pentru a implementa alte 3 functii: | Pentru a comunica cu **modulul Bluetooth HC-05** am folosit biblioteca SoftwareSerial.h prin intermediul careia am creat un obiect de tipul SoftwareSerial pe care am atasat pinii: 9(pentru RX) si 8(pentru TX). Din cadrul acestei clase am utilizat 3 functii importante: available(), write() si read(). Aceste functii le-am folosit pentru a implementa alte 3 functii: | ||
- | 1. **verify_bt_serial** = functie care este apelata in loop, si care are urmatoarea logica: asteapta sa se primeasca token-ul de inceput al mesajului(?), dupa ce primeste token-ul de inceput, citeste datele de pe seriala byte cu byte si le pune intr-un buffer pana cand primeste token-ul de final al mesajului(;), caz in care trimite mesajul catre functia de parsare si reseteaza buffer-ul si flag-ul de asteptare al inceputului unui nou mesaj. De asemenea, aceasta face si o verificare, in cazul in care mesajul pe care il primeste depaseste 20 de caractere(aplicatia nu va trimite atatea caractere in niciun caz), va ignora mesajul, pentru ca este posibil ca acesta este posibil sa contina date incorecte datorita conexiunii. | + | * **verify_bt_serial** = functie care este apelata in loop, si care are urmatoarea logica: asteapta sa se primeasca token-ul de inceput al mesajului(?), dupa ce primeste token-ul de inceput, citeste datele de pe seriala byte cu byte si le pune intr-un buffer pana cand primeste token-ul de final al mesajului(;), caz in care trimite mesajul catre functia de parsare si reseteaza buffer-ul si flag-ul de asteptare al inceputului unui nou mesaj. De asemenea, aceasta face si o verificare, in cazul in care mesajul pe care il primeste depaseste 20 de caractere(aplicatia nu va trimite atatea caractere in niciun caz), va ignora mesajul, pentru ca este posibil ca acesta este posibil sa contina date incorecte datorita conexiunii. |
- | 2. **parse_message** = functie in care parsam valorile primite intr-un mesaj, aceasta verifica in primul rand daca mesajul este in formatul in care il asteptam(fingers=xx&wrist=yyy), si daca salveaza cele doua valori in format string(xx si yyy) pentru ca ulterior sa fie prelucrate de functia de validare a datelor si executarea comenzii pe servomotoare. | + | * **parse_message** = functie in care parsam valorile primite intr-un mesaj, aceasta verifica in primul rand daca mesajul este in formatul in care il asteptam(fingers=xx&wrist=yyy), si daca salveaza cele doua valori in format string(xx si yyy) pentru ca ulterior sa fie prelucrate de functia de validare a datelor si executarea comenzii pe servomotoare. |
- | 3. **write_values_to_servo** = functie care valideaza cele doua string-uri de mai sus in 2 moduri:daca valoarea convertita de la string folosind functia toInt() este 0, si ultima valoarea scrisa pe motoras este ceva mult mai mare decat valori apropiate lui 0, este posibil ca valorile xx sau yyy sa fi continut caractere ciudate si de aceea returnul conversiei era 0, caz in care vom ignora valorile si vom astepta altele noim, iar, al doilea mod de validare consta in verificarea celor 2 valori sa fie in limitele permise: pentru degete [0, 90] grade si pentru incheietura [0, 180] grade. De asemenea functia verifica si daca noua valoare pentru un motor este egala cu cea veche, caz in care nu mai face write pentru ca ar fi inutil. Daca toate validarile merg ok, atunci noile valori sunt transmise catre servomotoare. | + | * **write_values_to_servo** = functie care valideaza cele doua string-uri de mai sus in 2 moduri:daca valoarea convertita de la string folosind functia toInt() este 0, si ultima valoarea scrisa pe motoras este ceva mult mai mare decat valori apropiate lui 0, este posibil ca valorile xx sau yyy sa fi continut caractere ciudate si de aceea returnul conversiei era 0, caz in care vom ignora valorile si vom astepta altele noim, iar, al doilea mod de validare consta in verificarea celor 2 valori sa fie in limitele permise: pentru degete [0, 90] grade si pentru incheietura [0, 180] grade. De asemenea functia verifica si daca noua valoare pentru un motor este egala cu cea veche, caz in care nu mai face write pentru ca ar fi inutil. Daca toate validarile merg ok, atunci noile valori sunt transmise catre servomotoare. |
Pentru **controlul servomotoarelor** am folosit libraria Servo.h cu care am creat 2 obiecte de tipul Servo: pe primul dintre ele(cel pentru incheietura) l-am atasat la pinul 10 si pe celalalt(cel pentru degete) la pinul 11 prin intermediul metodei attach din cadrul clasei. Pentru a transmite semnal catre servomotoare am folosit metoda write. | Pentru **controlul servomotoarelor** am folosit libraria Servo.h cu care am creat 2 obiecte de tipul Servo: pe primul dintre ele(cel pentru incheietura) l-am atasat la pinul 10 si pe celalalt(cel pentru degete) la pinul 11 prin intermediul metodei attach din cadrul clasei. Pentru a transmite semnal catre servomotoare am folosit metoda write. | ||
+ | |||
Pentru a folosi **LCD-ul** am folosit libraria LiquidCrystal_I2C.h, creand un obiect de tipul LiquidCrystal_I2C pentru care am aflat Slave Address-ul necesar protocolului I2C. Ca si functii utilizate din aceasta biblioteca sunt: init, backlight, setCursor si print. Datele de pe ecran sunt reprezentate de cele doua unghiuri, respectiv niste avertismente atunci cand se ating cele 2 limite(90/180 de grade) si le-am actualizat la un interval de o secunda prin intermediul unui timer pe care il voi descrie mai jos. | Pentru a folosi **LCD-ul** am folosit libraria LiquidCrystal_I2C.h, creand un obiect de tipul LiquidCrystal_I2C pentru care am aflat Slave Address-ul necesar protocolului I2C. Ca si functii utilizate din aceasta biblioteca sunt: init, backlight, setCursor si print. Datele de pe ecran sunt reprezentate de cele doua unghiuri, respectiv niste avertismente atunci cand se ating cele 2 limite(90/180 de grade) si le-am actualizat la un interval de o secunda prin intermediul unui timer pe care il voi descrie mai jos. | ||
+ | |||
Dupa cum am specificat si in descrierea aplicatiei de Android, placuta trimite un semnal periodic(1 sec) telefonului, pentru ca utilizatorul sa stie ca, conexiunea este inca stabila si toate comenzile pe care le trimite ajung la placuta. Am realizat acest lucru periodic, tot cu ajutorului unui timer configurat prin intermediul **Timer0**, astfel incat sa genereze 250 de intreruperi pe secunda, si la cea de-a 250-a sa realizeze atat actualizarea datelor de pe LCD, cat si transmiterea semnalului catre telefon. Pentru a-l configura am definit functia **init_timer0_one_sec**, in care am pus modul de functionare(CTC), valoarea prescaler-ului(256) si valoarea pragului de numarare(250), respectiv activarea intreruperii atunci cand counter-ul ajunge la un anumit prag, iar in rutina de tratare a intreruperii **ISR(TIMER0_COMPA_vect)** doar verificam unde am ajuns cu celalalt counter, si daca s-a ajuns la 250(adica a trecut o secunda), avem un flag pe care il facem true si prin care anuntam functia loop ca poate face transmisia catre telefon si afisarea pe LCD. | Dupa cum am specificat si in descrierea aplicatiei de Android, placuta trimite un semnal periodic(1 sec) telefonului, pentru ca utilizatorul sa stie ca, conexiunea este inca stabila si toate comenzile pe care le trimite ajung la placuta. Am realizat acest lucru periodic, tot cu ajutorului unui timer configurat prin intermediul **Timer0**, astfel incat sa genereze 250 de intreruperi pe secunda, si la cea de-a 250-a sa realizeze atat actualizarea datelor de pe LCD, cat si transmiterea semnalului catre telefon. Pentru a-l configura am definit functia **init_timer0_one_sec**, in care am pus modul de functionare(CTC), valoarea prescaler-ului(256) si valoarea pragului de numarare(250), respectiv activarea intreruperii atunci cand counter-ul ajunge la un anumit prag, iar in rutina de tratare a intreruperii **ISR(TIMER0_COMPA_vect)** doar verificam unde am ajuns cu celalalt counter, si daca s-a ajuns la 250(adica a trecut o secunda), avem un flag pe care il facem true si prin care anuntam functia loop ca poate face transmisia catre telefon si afisarea pe LCD. | ||
+ | |||
In functia de **setup** apelam functia de initializare a timer-ului, configuram baudrate-ul serialei de comunicare cu modulul de bluetooth, initializam LCD-ul, respectiv cele 2 servomotoare. | In functia de **setup** apelam functia de initializare a timer-ului, configuram baudrate-ul serialei de comunicare cu modulul de bluetooth, initializam LCD-ul, respectiv cele 2 servomotoare. | ||
+ | |||
In functia de **loop**, apelam functia de verificare a serialei cu modulul(verify_bt_serial), care la randul ei le apela pe celelalte cand era nevoie, adica cand s-a primit vreo comanda, respectiv, verifica flag-ul pentru a vedea daca trebuie actualizat LCD-ul sau trimis semnal catre telefon. | In functia de **loop**, apelam functia de verificare a serialei cu modulul(verify_bt_serial), care la randul ei le apela pe celelalte cand era nevoie, adica cand s-a primit vreo comanda, respectiv, verifica flag-ul pentru a vedea daca trebuie actualizat LCD-ul sau trimis semnal catre telefon. | ||
+ | |||
+ | ===== Rezultate obtinute ===== | ||
+ | Rezulatul este o mana controlabila din telefon. Pentru cei care se uita va rog sa nu radeti, numai eu stiu cate straturi de superglue am pe degete :(. Multumesc. | ||
+ | |||
+ | Scurt demo cu proiectul: | ||
+ | [[https://youtube.com/shorts/-CtTY38AmgQ?feature=share|Demo Bionic Hand]] | ||
+ | |||
+ | Ca si observatie, motorasele se invart corect, dar scheletul mainii esti putin fragil si de aceea nu se observa chiar cum as fi vrut. | ||
+ | |||
+ | {{https://ocw.cs.pub.ro/courses/_media/pm/prj2023/iotelea/bionic_hand_1.jpeg?450x500}} | ||
+ | {{https://ocw.cs.pub.ro/courses/_media/pm/prj2023/iotelea/bionic_hand_2.jpeg?450x500}} | ||
+ | |||
+ | ===== Concluzii ===== | ||
+ | Realizarea proiectului a constat intr-o munca constanta pe parcursul mai multor zile, ca si probleme am avut in special la parsarea mesajelor primite prin Bluetooth, pentru ca uneori primeam mesaje incorecte datorita conexiunii si a durat ceva pana cand mi-am dat seama ca trebuie sa verific corectitudinea acestora. O alta problema intalnita a fost legata de timere, pentru ca incercam sa folosesc Timer1 pentru implementarea mea fara sa-mi dau seama ca acesta este folosit in spate de biblioteca Servo.h. Partea de design al proiectului, a fost destul de grea, dar in acelasi timp interesanta, sa iti imaginezi cum poti face un lucru din diferite materiale sa arate a ceva ce ti-ai propus si sa functioneze cum iti imaginai. | ||
+ | |||
+ | ===== Download ===== | ||
+ | Codul sursa pentru placuta si pentru aplicatia de telefon se regasesc in urmatoarea arhiva: | ||
+ | |||
+ | {{https://ocw.cs.pub.ro/courses/_media/pm/prj2023/iotelea/Bionic_Hand_Project.zip}} | ||
===== Jurnal ===== | ===== Jurnal ===== | ||
*3.05.2023: Crearea paginii proiectului si descrierea sumara a functionalitatilor acestuia, motivatai din spate si scopul pe care il are. | *3.05.2023: Crearea paginii proiectului si descrierea sumara a functionalitatilor acestuia, motivatai din spate si scopul pe care il are. | ||
*18.05.2023: Completarea paginii cu partea de Hardware Design si conectarea fizica a componentelor. Testarea componentelor pe rand pentru a verifica daca functioneaza corect, actualizarea schemei de hardware design cu mici diferente in ceea ce priveste piesele folosite si actualizarea si listei in care le-am specificat. | *18.05.2023: Completarea paginii cu partea de Hardware Design si conectarea fizica a componentelor. Testarea componentelor pe rand pentru a verifica daca functioneaza corect, actualizarea schemei de hardware design cu mici diferente in ceea ce priveste piesele folosite si actualizarea si listei in care le-am specificat. | ||
+ | *25.05.2023: Adaugarea de surse de alimentare suplimentare pentru fiecare servomotor. | ||
+ | *27.05.2023: Modificari finale asupra circuitului si a codului. | ||
+ | *28.05.2023: Crearea de elemente pentru design-ul mainii si actualizarea paginii proiectului. | ||
+ | |||
+ | ===== Bibliografie/Resurse ===== | ||
+ | |||
+ | * [[https://ocw.cs.pub.ro/courses/pm/lab/lab3-2023|Lab PM 3]] | ||
+ | * [[https://ocw.cs.pub.ro/courses/pm/lab/lab5-2023|Lab PM 5]] | ||
+ | * [[https://ocw.cs.pub.ro/courses/pm/lab/lab6-2022|Lab PM 6]] | ||
+ | * [[https://www.youtube.com/watch?v=jGoKdF8r7as|Creare aplicatie Android]] | ||
+ | * [[https://www.youtube.com/watch?v=8GI17HWXdRI&list=LL&index=27&t=94s|Reglare contrast LCD]] | ||
<html><a class="media mediafile mf_pdf" href="https://ocw.cs.pub.ro/courses/pm/prj2023/iotelea/bionic_hand?do=export_pdf">Export to PDF</a></html> | <html><a class="media mediafile mf_pdf" href="https://ocw.cs.pub.ro/courses/pm/prj2023/iotelea/bionic_hand?do=export_pdf">Export to PDF</a></html> | ||