This shows you the differences between two versions of the page.
pm:prj2023:adarmaz:sistem-cartografiere-sol [2023/05/29 19:28] ioana.profeanu [Configurare întrerupere buton joystick] |
pm:prj2023:adarmaz:sistem-cartografiere-sol [2023/05/30 09:13] (current) ioana.profeanu [Download cod] |
||
---|---|---|---|
Line 1: | Line 1: | ||
====== Sistem Cartografiere Sol - Profeanu Ioana 333CA ====== | ====== Sistem Cartografiere Sol - Profeanu Ioana 333CA ====== | ||
===== Introducere ===== | ===== Introducere ===== | ||
- | Proiectul constă într-un sistem inteligent de monitorizare și înregistrare a diferșilor parametri necesari dezvoltării plantelor, date care, utilizând o bază de date predefinită (stocată pe un card micro SD) cu plante și condițiile lor de mediu, pot fi comparate și interogate de utilizator, acesta putând să vadă o listă a plantelor viabile, să vadă toate plantele din baza de date, să afiseze sau reseteze media metricilor. | + | Proiectul constă într-un sistem inteligent de monitorizare și înregistrare a diferșilor parametri necesari dezvoltării plantelor, date care, utilizând o bază de date predefinită (stocată pe un card micro SD) cu plante și condițiile lor de mediu, pot fi interogate de utilizator, acesta putând să vadă o listă a plantelor viabile, să vadă toate plantele din baza de date, să afiseze sau reseteze media metricilor. |
Sistemul este extrem de util atât pentru cei din domeniul agriculturii, pentru a identifica plantele ce pot crește într-un anumit tip de sol și mediu, precum și pentru cei care își doresc să cultive diverse plante în grădină și vor să afle ce opțiuni au, sau să vadă daca o anumită plantă poate supraviețui condițiilor. | Sistemul este extrem de util atât pentru cei din domeniul agriculturii, pentru a identifica plantele ce pot crește într-un anumit tip de sol și mediu, precum și pentru cei care își doresc să cultive diverse plante în grădină și vor să afle ce opțiuni au, sau să vadă daca o anumită plantă poate supraviețui condițiilor. | ||
- | Ideea a pornit de la nevoia de a cultiva plante într-un sol și mediu care să fie propice dezvoltării lor, sistemul ținând cont nu doar de climă, ci și de factorii impredictibili de mediu (seceta, inundații etc). | + | Ideea a pornit de la nevoia de a cultiva plante într-un sol și mediu care să fie propice dezvoltării lor, sistemul ținând cont nu doar de climă, ci și de factorii impredictibili de mediu (secetă, inundații etc). |
===== Descriere generală ===== | ===== Descriere generală ===== | ||
- | Inițial, sistemul se află în starea de stand by, unde afișeaza date pe ecran despre condițiile de mediu și coordonatele GPS. Acestea sunt recalculate la interval de o oră și adăugate unei medii generale. Tot atunci, mediile generale ale factorilor de meniu sunt inregistrați într-un fișier de log pe cardul SD. | + | Inițial, sistemul se află în starea de stand by, unde afișează date pe ecran despre condițiile de mediu și coordonatele GPS. Acestea sunt recalculate la interval de o oră și adăugate unei medii generale. Tot atunci, mediile generale ale factorilor de meniu sunt inregistrați într-un fișier de log pe cardul SD. |
La apăsarea butonului joystick-ului, se afișează 5 opțiuni de meniuri, fiecare apărând pe ecran la interval de 6 secunde. Selectarea meniului dorit se face apăsând din nou pe buton, în timp ce meniul dorit este afișat pe ecran. | La apăsarea butonului joystick-ului, se afișează 5 opțiuni de meniuri, fiecare apărând pe ecran la interval de 6 secunde. Selectarea meniului dorit se face apăsând din nou pe buton, în timp ce meniul dorit este afișat pe ecran. | ||
Line 52: | Line 52: | ||
* SoftwareSerial.h - pentru citirea datelor primite de la modulul GPS | * SoftwareSerial.h - pentru citirea datelor primite de la modulul GPS | ||
+ | ==== Flow program ==== | ||
+ | În set-up, se inițializează timer-ul, întreruperea butonului, ecranul OLED și se apelează funcția de citire și afișare a datelor de mediu pe ecran. Tot atunci se și loghează pe cardul SD metricile citie. | ||
+ | |||
+ | În loop, se verifică flag-ul pentru întreruperea pe butonul joystick-ului. În acest caz, se pornește afișarea meniurilor o data la 6 secunde. Dacă se depășesc 3 minute de la ultimul input al user-ului, recitesc datele de mediu și se revine la afișarea lor. | ||
+ | |||
+ | Dacă în fereastra de 3 minute user-ul apasa din nou pe buton, se va face acțiunea afișată pe display în acel moment (se verifică displayFrame-ul curent la momentul apăsării). | ||
+ | |||
+ | În lipsa vreunui input de la utilizator, se verifică dacă au trecut 60 de minute de la ultima citire a datelor, iar daca da, se face recitirea. | ||
+ | |||
+ | Programul face diverse verificări și afișări de mesaje pe ecran în cazurile speciale/de eroare, precum căutarea unei plante după resetarea metricilor, imposibilitatea de a citi cardul SD (de exemplu, dacă acesta nu este introdus) și dacă nu există nicio plantă care să poată fi plantată în mediul curent. | ||
==== Configurare timer ==== | ==== Configurare timer ==== | ||
- | Deoarece în program am nevoie să contorizez diferite acțiuni (recalcularea metricilor o data la o oră, afișarea unui nou meniu la 6 secunde distanță, revenirea la meniul de stand-by in cazul neinteracțiunii user-ului timp de 3 minute). Am ales să configurez un timer utilizând Timer1, ce numără aproximativ 2 secunde, utilizând apoi mai multe variabile globale care contorizează de câte ori au trecut cele 2 secunde. | + | Deoarece în program am nevoie să contorizez diferite acțiuni (recalcularea metricilor o data la o oră, afișarea unui nou meniu la 6 secunde distanță, revenirea la meniul de stand-by in cazul neinteracțiunii user-ului timp de 3 minute), am ales să configurez un timer utilizând Timer1, ce numără aproximativ 2 secunde, folosind un prescaler de 1024 și CTC, utilizând apoi mai multe variabile globale care contorizează de câte ori au trecut cele 2 secunde. |
<spoiler Cod timer> | <spoiler Cod timer> | ||
<code cpp> | <code cpp> | ||
// timer contorizare durata de afisaj a statisticilor în stand-by | // timer contorizare durata de afisaj a statisticilor în stand-by | ||
- | unsigned long int timerDisplayInfo = 0; | + | volatile unsigned long int timerDisplayInfo = 0; |
// timer contorizare timp de la ultimul input al user-ului | // timer contorizare timp de la ultimul input al user-ului | ||
- | int timerUser = 0; | + | volatile int timerUser = 0; |
// contorizare timpul unui frame | // contorizare timpul unui frame | ||
- | int frameTime = 0; | + | volatile int frameTime = 0; |
ISR(TIMER1_COMPA_vect) { | ISR(TIMER1_COMPA_vect) { | ||
Line 91: | Line 101: | ||
\\ | \\ | ||
- | Apoi, în loop, variabilele globale de contorizare a timpului sunt verificate, și în funcție de acestea sunt afișate datele pe ecranul OLED / se execută diverse acțiuni. | + | Apoi, în loop, variabilele globale de contorizare a timpului sunt verificate și în funcție de acestea sunt afișate datele pe ecranul OLED / se execută diverse acțiuni. |
==== Configurare întrerupere buton joystick ==== | ==== Configurare întrerupere buton joystick ==== | ||
Line 140: | Line 150: | ||
Pentru lucrul cu cardul SD, am utilizat biblioteca SD.h, ce pune la dispoziție funcții de bază pentru scrierea și citirea datelor de pe cardul SD. | Pentru lucrul cu cardul SD, am utilizat biblioteca SD.h, ce pune la dispoziție funcții de bază pentru scrierea și citirea datelor de pe cardul SD. | ||
+ | |||
+ | === Scrierea metricilor medii === | ||
+ | |||
+ | La fiecare recalculare a metricilor, mediile datelor citite sunt stocate și pe cardul SD într-un fișier de log numit metrics.txt, inserându-se media temperaturii, a umitității aerului și solului, a luminozității și numărul de citiri. | ||
<spoiler Cod scriere metrici pe cardul SD> | <spoiler Cod scriere metrici pe cardul SD> | ||
<code cpp> | <code cpp> | ||
- | void writeMetricsToSD(){ | + | // function which writes the average metrics to sd card |
+ | void writeMetricsToSD() | ||
+ | { | ||
if (SD.begin(7)) { | if (SD.begin(7)) { | ||
File myFile; | File myFile; | ||
Line 177: | Line 193: | ||
</code> | </code> | ||
</spoiler> | </spoiler> | ||
- | |||
- | === Scrierea metricilor medii === | ||
- | |||
- | La fiecare recalculare a metricilor, mediile datelor citite sunt stocate și pe cardul SD într-un fișier de log numit metrics.txt, inserându-se media temperaturii, a umitității aerului și solului, a luminozității și numărul de citiri. | ||
=== Citirea plantelor === | === Citirea plantelor === | ||
Line 191: | Line 203: | ||
//**Lavender, 3, 10, 30, 30, 60, 1, 3, 1, 3**// | //**Lavender, 3, 10, 30, 30, 60, 1, 3, 1, 3**// | ||
+ | |||
+ | Temperatura este măsurată în ºC, iar umiditatea aerului în procente. | ||
Datele cu valori între 1-4 au următoarele semnificații: | Datele cu valori între 1-4 au următoarele semnificații: | ||
Line 200: | Line 214: | ||
| 4 | Polar | | | 4 | Polar | | ||
- | Pentru modul de afișare a plantelor ce pot trăi în mediul curent, se compară valorile medii pentru fiecare dintre categoriile de metrici, afișându-se doar plantele care se încadrează în datele stocate pe cardul SD. | + | Pentru modul de afișare a plantelor ce pot trăi în mediul curent, se compară valorile medii pentru fiecare dintre categoriile de metrici, primul criteriu de comparare fiind clima, afișându-se doar plantele care se încadrează în parametrii dați. |
Pentru modul de afișare a tututor plantelor, se afișează pe ecran toate plantele, fără a face vreo filtrare. | Pentru modul de afișare a tututor plantelor, se afișează pe ecran toate plantele, fără a face vreo filtrare. | ||
Line 208: | Line 222: | ||
<code cpp> | <code cpp> | ||
- | void displayPlantsList(bool showAll) { | + | // functions which displays the valid plants |
- | display.clear(); // Clear the display | + | // on the display |
- | display.setCursor(0, 0); // Set the cursor position | + | void displayPlantsList(bool showAll) |
- | if (SD.begin(7)){ | + | { |
+ | display.clear(); | ||
+ | display.setCursor(0, 0); | ||
+ | // try to read from SD card | ||
+ | if (SD.begin(7)) { | ||
File myFile; | File myFile; | ||
- | if(SD.exists("plants.txt")) { | + | if (SD.exists("plants.txt")) { |
myFile = SD.open("plants.txt"); | myFile = SD.open("plants.txt"); | ||
- | + | // if the file exists, read from it | |
- | if(myFile) { | + | if (myFile) { |
int noPlantsOk = 0; | int noPlantsOk = 0; | ||
- | + | // parse the data of the plant by extracting it; | |
- | while(myFile.available()) { | + | // each data is delimited by a comma |
+ | while (myFile.available()) { | ||
bool okPlant = false; | bool okPlant = false; | ||
String line = myFile.readStringUntil('\n'); | String line = myFile.readStringUntil('\n'); | ||
Line 225: | Line 244: | ||
int values[9]; | int values[9]; | ||
int index = 0; | int index = 0; | ||
+ | // read line | ||
while (line.length() > 0) { | while (line.length() > 0) { | ||
int commaIndex = line.indexOf(','); | int commaIndex = line.indexOf(','); | ||
- | | ||
if (commaIndex != -1) { | if (commaIndex != -1) { | ||
String element = line.substring(0, commaIndex); | String element = line.substring(0, commaIndex); | ||
- | line = line.substring(commaIndex + 2); // Skip comma and space after each value | + | line = line.substring(commaIndex + 2); |
| | ||
if (index == 0) { | if (index == 0) { | ||
Line 237: | Line 255: | ||
} else { | } else { | ||
values[index - 1] = element.toInt(); | values[index - 1] = element.toInt(); | ||
+ | // check if the plant is between parameters | ||
if (showAll == false) { | if (showAll == false) { | ||
if (index == 1) { | if (index == 1) { | ||
Line 249: | Line 267: | ||
if (index == 3) { | if (index == 3) { | ||
int mediumTemp = sumTemps / timesRead; | int mediumTemp = sumTemps / timesRead; | ||
- | if ((mediumTemp >= values[1] && mediumTemp <= values[2]) == false) { | + | if ((mediumTemp >= values[1] && mediumTemp <= values[2]) |
+ | == false) { | ||
break; | break; | ||
} | } | ||
Line 256: | Line 275: | ||
if (index == 5) { | if (index == 5) { | ||
int mediumAirMoist = sumAirMoist / timesRead; | int mediumAirMoist = sumAirMoist / timesRead; | ||
- | if ((mediumAirMoist >= values[3] && mediumAirMoist <= values[4]) == false) { | + | if ((mediumAirMoist >= values[3] && mediumAirMoist |
+ | <= values[4]) == false) { | ||
break; | break; | ||
} | } | ||
Line 263: | Line 283: | ||
if (index == 7) { | if (index == 7) { | ||
int mediumSoilMoist = sumSoilMoist / timesRead; | int mediumSoilMoist = sumSoilMoist / timesRead; | ||
- | if ((mediumSoilMoist >= values[5] && mediumSoilMoist <= values[6]) == false) { | + | if ((mediumSoilMoist >= values[5] && mediumSoilMoist |
+ | <= values[6]) == false) { | ||
break; | break; | ||
} | } | ||
Line 272: | Line 293: | ||
index++; | index++; | ||
} else { | } else { | ||
- | // Last value in the line (no comma at the end) | + | // read last value from line |
String element = line; | String element = line; | ||
values[index - 1] = element.toInt(); | values[index - 1] = element.toInt(); | ||
int mediumLight = sumLight / timesRead; | int mediumLight = sumLight / timesRead; | ||
- | if (showAll == false && (mediumLight >= values[7] && mediumLight <= values[8]) == false) { | + | if (showAll == false && (mediumLight >= values[7] |
+ | && mediumLight <= values[8]) == false) { | ||
break; | break; | ||
} | } | ||
+ | |||
okPlant = true; | okPlant = true; | ||
noPlantsOk++; | noPlantsOk++; | ||
Line 284: | Line 307: | ||
} | } | ||
} | } | ||
+ | // if a plant was found, display it | ||
if (okPlant) { | if (okPlant) { | ||
- | checkAndDisplayPlant(name, values); | + | displayPlantDetails(name, values); |
} | } | ||
} | } | ||
+ | // if no plants were found | ||
if (noPlantsOk == 0) { | if (noPlantsOk == 0) { | ||
display.println("No plants matched!"); | display.println("No plants matched!"); | ||
- | display.displayRows(); // Update the display | + | display.displayRows(); |
delay(8000); | delay(8000); | ||
} | } | ||
Line 296: | Line 321: | ||
| | ||
} else { | } else { | ||
+ | // if the file is not found | ||
display.println("Add plants.txt file first!"); | display.println("Add plants.txt file first!"); | ||
- | display.displayRows(); // Update the display | + | display.displayRows(); |
delay(8000); | delay(8000); | ||
} | } | ||
} | } | ||
} else { | } else { | ||
+ | // if unable to read file | ||
display.println("Failed to display plants!"); | display.println("Failed to display plants!"); | ||
- | display.displayRows(); // Update the display | + | display.displayRows(); |
delay(8000); | delay(8000); | ||
} | } | ||
} | } | ||
- | | ||
</code> | </code> | ||
</spoiler> | </spoiler> | ||
- | \\ | ||
==== Parsare date GPS ==== | ==== Parsare date GPS ==== | ||
- | Pentru a utiliza modulul GPS NEO-6M, am folosit biblioteca SoftwareSerial.h. Deși în mod normal modulul utilizează USART, nu am folosit cei doi pini dedicați ai Arduino-ului, deoarece acest lucru m-ar fi împiedicat din a face debugging utilizând afișarea la serială. | + | Pentru a utiliza modulul GPS NEO-6M, am folosit biblioteca SoftwareSerial.h. Deși în mod normal modulul utilizează USART, nu am folosit pinii RX și TX Arduino-ului, deoarece acest lucru m-ar fi împiedicat din a face debugging utilizând afișarea serială. |
Am decis, de asemenea, să îmi implementez propria funcție de parsare a input-ului primit de la GPS, din considerente de memorie (introducerea unei biblioteci adiționale ar fi costat memorie), dar și pentru că doream să extrag doar latitudinea din input-ul primit (doar de aceasta este nevoie pentru a determina clima). | Am decis, de asemenea, să îmi implementez propria funcție de parsare a input-ului primit de la GPS, din considerente de memorie (introducerea unei biblioteci adiționale ar fi costat memorie), dar și pentru că doream să extrag doar latitudinea din input-ul primit (doar de aceasta este nevoie pentru a determina clima). | ||
Line 322: | Line 347: | ||
<code cpp> | <code cpp> | ||
- | // function for extracting the latitude | + | // function which, from an input NMEA sentence, |
- | String extractLatitude(const String& sentence) { | + | // extracts a string with the latitude |
+ | String extractCoordinates(const String& sentence) | ||
+ | { | ||
int commas; | int commas; | ||
if (sentence.indexOf("GPRMC") != -1) { | if (sentence.indexOf("GPRMC") != -1) { | ||
Line 337: | Line 364: | ||
int endIndex = -1; | int endIndex = -1; | ||
+ | // count commas and extract the value of the latitude | ||
for (size_t i = 0; i < sentence.length(); i++) { | for (size_t i = 0; i < sentence.length(); i++) { | ||
if (sentence[i] == ',') { | if (sentence[i] == ',') { | ||
Line 356: | Line 384: | ||
} | } | ||
- | // function for receiving NMEA sentences | + | // function which reads NMEA sentences from |
+ | // software serial for 20 seconds and returns the | ||
+ | // latitude in case the sentence is valid | ||
String getGPGGAsentence() | String getGPGGAsentence() | ||
{ | { | ||
Line 365: | Line 395: | ||
gpsSerial.begin(9600); | gpsSerial.begin(9600); | ||
- | // repeat the reading for 20 seconds | ||
while (timerDisplayInfo - currentTime < MINUTE / 4) { | while (timerDisplayInfo - currentTime < MINUTE / 4) { | ||
if (gpsSerial.available()) { | if (gpsSerial.available()) { | ||
Line 389: | Line 418: | ||
return sentence; | return sentence; | ||
} | } | ||
- | |||
</code> | </code> | ||
</spoiler> | </spoiler> | ||
Line 439: | Line 467: | ||
===== Download cod ===== | ===== Download cod ===== | ||
+ | Codul poate fi vizualizat [[https://github.com/ioanaprofeanu/Sistem-cartografiere-sol.git|aici]] sau descărcat {{:pm:prj2023:adarmaz:sistem_cartografiere_sol.zip|aici}}. | ||
===== Jurnal ===== | ===== Jurnal ===== | ||
Line 446: | Line 475: | ||
* 5-8 mai - realizare parte hardware | * 5-8 mai - realizare parte hardware | ||
* 10-20 mai - realizare parte software | * 10-20 mai - realizare parte software | ||
+ | * 28-29 mai - finalizare documentație | ||
===== Bibliografie/Resurse ===== | ===== Bibliografie/Resurse ===== | ||
Line 454: | Line 484: | ||
* http://educ8s.tv/arduino-sd-card-tutorial/ | * http://educ8s.tv/arduino-sd-card-tutorial/ | ||
* https://www.youtube.com/watch?v=qKku-mmwNIA&list=LL&index=8&ab_channel=BINARYUPDATES | * https://www.youtube.com/watch?v=qKku-mmwNIA&list=LL&index=8&ab_channel=BINARYUPDATES | ||
+ | * https://docs.arduino.cc/learn/built-in-libraries/software-serial | ||
+ | * https://content.meteoblue.com/en/research-education/educational-resources/meteoscool/general-climate-zones | ||
+ | * ChatGPT pentru găsirea plantelor care să se încadreze în anumitele tipuri de climă | ||
<html><a class="media mediafile mf_pdf" href="?do=export_pdf">Export to PDF</a></html> | <html><a class="media mediafile mf_pdf" href="?do=export_pdf">Export to PDF</a></html> |