Ultima lucrare combină toate noțiunile studiate în aplicații complete: achiziție de la senzori, comandă de actuatoare de putere, comunicație wireless și măsurarea distanței. Fiecare aplicație folosește cel puțin trei dintre modulele studiate anterior.
1Obiectivele lucrării
- Integrarea perifericelor studiate într-o aplicație funcțională
- Realizarea unei stații meteo cu afișarea datelor
- Comanda unui motor de curent continuu cu reglaj de turație și sens
- Transmiterea datelor între două plăci prin modul radio
- Măsurarea distanței cu senzor ultrasonic și compensarea cu temperatura
- Structurarea unui program complex ca automat de stări
2Materiale necesare
- 2 Arduino Uno
- 1 Senzor DHT11 / DHT22
- 1 Motor DC + punte H L298N
- 2 Module nRF24L01
- 1 Senzor HC-SR04
- 1 Potențiometru 10 kΩ
- 1 Sursă externă 7–12 V
- 2 Condensatoare 10 µF
3Aplicația 1 - Stație meteo
Senzorul DHT măsoară temperatura și umiditatea, transmițând datele printr-un protocol propriu, pe un singur fir. Temporizările sunt foarte stricte (de ordinul microsecundelor), motiv pentru care se folosește o bibliotecă dedicată.
| Parametru | DHT11 | DHT22 |
|---|---|---|
| Domeniu temperatură | 0…50 °C | −40…80 °C |
| Precizie temperatură | ±2 °C | ±0,5 °C |
| Domeniu umiditate | 20…90 % | 0…100 % |
| Rezoluție | 1 °C / 1 % | 0,1 °C / 0,1 % |
| Interval minim între citiri | 1 s | 2 s |
| Alimentare | 3,3–5,5 V | 3,3–6 V |
NaN sau valoarea anterioară, memorată intern. Verificați întotdeauna cu
isnan() înainte de a folosi rezultatul.
#include <DHT.h>
#define PIN_DHT 7
#define TIP_DHT DHT11
DHT senzor(PIN_DHT, TIP_DHT);
unsigned long ultimaCitire = 0;
const unsigned long INTERVAL = 2000; // respecta intervalul minim
float tMin = 999, tMax = -999; // valori extreme de la pornire
void setup() {
Serial.begin(9600);
senzor.begin();
Serial.println("Statie meteo pornita");
}
void loop() {
if (millis() - ultimaCitire < INTERVAL) return; // temporizare neblocanta
ultimaCitire = millis();
float umiditate = senzor.readHumidity();
float temperatura = senzor.readTemperature();
if (isnan(umiditate) || isnan(temperatura)) { // VERIFICARE obligatorie
Serial.println("Eroare la citirea senzorului!");
return;
}
if (temperatura < tMin) tMin = temperatura;
if (temperatura > tMax) tMax = temperatura;
float indiceCaldura = senzor.computeHeatIndex(temperatura, umiditate, false);
Serial.print("T = "); Serial.print(temperatura, 1);
Serial.print(" C RH = "); Serial.print(umiditate, 1);
Serial.print(" % resimtit = "); Serial.print(indiceCaldura, 1);
Serial.print(" C [min "); Serial.print(tMin, 1);
Serial.print(" / max "); Serial.print(tMax, 1);
Serial.println("]");
}
4Aplicația 2 - Control motor de curent continuu
Un motor nu poate fi alimentat direct de la un pin al microcontrolerului: consumă sute de miliamperi, în timp ce un pin livrează maximum 40 mA. Se folosește o punte H, care permite atât reglarea turației prin PWM, cât și inversarea sensului de rotație.
| IN1 | IN2 | ENA (PWM) | Efect |
|---|---|---|---|
| 0 | 0 | orice | oprit - rotire liberă (roată liberă) |
| 1 | 0 | 0…255 | rotire înainte, cu turația dată de PWM |
| 0 | 1 | 0…255 | rotire înapoi |
| 1 | 1 | orice | frânare activă (scurtcircuitarea bornelor) |
5Simulator de turație
Reglați factorul de umplere și urmăriți tensiunea medie aplicată motorului, precum și forma de undă rezultată. Comparați valoarea afișată cu pragul minim de pornire pe care îl veți determina experimental.
const uint8_t IN1 = 4, IN2 = 5, ENA = 9; // ENA trebuie sa fie pin PWM
const uint8_t PIN_POT = A0;
const uint8_t DUTY_MINIM = 60; // sub aceasta valoare motorul nu porneste
void setSens(bool inainte) {
digitalWrite(IN1, inainte ? HIGH : LOW);
digitalWrite(IN2, inainte ? LOW : HIGH);
}
void opreste() {
digitalWrite(IN1, LOW);
digitalWrite(IN2, LOW);
analogWrite(ENA, 0);
}
void setup() {
pinMode(IN1, OUTPUT);
pinMode(IN2, OUTPUT);
pinMode(ENA, OUTPUT);
Serial.begin(9600);
opreste();
}
void loop() {
uint16_t brut = analogRead(PIN_POT);
// zona moarta la mijloc: sub 490 inapoi, peste 530 inainte, intre ele oprit
if (brut < 490) {
setSens(false);
uint8_t duty = map(brut, 489, 0, DUTY_MINIM, 255);
analogWrite(ENA, duty);
Serial.print("INAPOI duty="); Serial.println(duty);
}
else if (brut > 530) {
setSens(true);
uint8_t duty = map(brut, 531, 1023, DUTY_MINIM, 255);
analogWrite(ENA, duty);
Serial.print("INAINTE duty="); Serial.println(duty);
}
else {
opreste();
Serial.println("OPRIT");
}
delay(100);
}
6Aplicația 3 - Comunicație radio
Modulul nRF24L01 lucrează în banda de 2,4 GHz și comunică cu microcontrolerul prin magistrala SPI. Permite legături bidirecționale pe distanțe de zeci de metri și confirmă automat recepția pachetelor.
| Pin nRF24L01 | Arduino Uno | Rol |
|---|---|---|
| VCC | 3,3 V | alimentare - nu 5 V! |
| GND | GND | masă |
| CE | D9 | activare emisie/recepție |
| CSN | D10 | selecție SPI (Slave Select) |
| SCK | D13 | ceas SPI |
| MOSI | D11 | date către modul |
| MISO | D12 | date de la modul |
#include <SPI.h>
#include <RF24.h>
RF24 radio(9, 10); // CE, CSN
const byte adresa[6] = "NOD01";
struct Pachet { // structura trebuie IDENTICA pe ambele placi
uint16_t contor;
int16_t temperatura; // in zecimi de grad, ca intreg
bool buton;
};
Pachet date = {0, 0, false};
void setup() {
Serial.begin(9600);
pinMode(2, INPUT_PULLUP);
radio.begin();
radio.openWritingPipe(adresa);
radio.setPALevel(RF24_PA_LOW); // putere redusa pentru teste pe masa
radio.setDataRate(RF24_250KBPS); // viteza mica -> raza mai mare
radio.stopListening(); // mod emisie
}
void loop() {
date.contor++;
date.temperatura = (int16_t)(analogRead(A0) * 0.489); // exemplu
date.buton = !digitalRead(2);
bool trimis = radio.write(&date, sizeof(date));
Serial.print("Pachet "); Serial.print(date.contor);
Serial.println(trimis ? " - confirmat" : " - ESUAT");
delay(500);
}
#include <SPI.h>
#include <RF24.h>
RF24 radio(9, 10);
const byte adresa[6] = "NOD01";
struct Pachet {
uint16_t contor;
int16_t temperatura;
bool buton;
};
Pachet date;
uint16_t ultimulContor = 0;
void setup() {
Serial.begin(9600);
pinMode(3, OUTPUT); // LED pe alt pin: D9-D13 sunt ocupati de SPI
radio.begin();
radio.openReadingPipe(0, adresa);
radio.setPALevel(RF24_PA_LOW);
radio.setDataRate(RF24_250KBPS);
radio.startListening(); // mod receptie
}
void loop() {
if (radio.available()) {
radio.read(&date, sizeof(date));
digitalWrite(3, date.buton);
// detectarea pachetelor pierdute
if (date.contor != ultimulContor + 1 && ultimulContor != 0) {
Serial.print("!! pierdute ");
Serial.print(date.contor - ultimulContor - 1);
Serial.println(" pachete");
}
ultimulContor = date.contor;
Serial.print("Pachet #"); Serial.print(date.contor);
Serial.print(" T = "); Serial.print(date.temperatura / 10.0, 1);
Serial.print(" C buton = "); Serial.println(date.buton ? "apasat" : "liber");
}
}
7Aplicația 4 - Măsurarea distanței
Senzorul HC-SR04 emite un impuls ultrasonic de 40 kHz și măsoară timpul până la întoarcerea ecoului. Cunoscând viteza sunetului, se calculează distanța până la obstacol.
| Parametru | Valoare | Observații |
|---|---|---|
| Domeniu de măsurare | 2 cm … 4 m | sub 2 cm ecoul se suprapune peste emisie |
| Precizie | ±3 mm | în condiții ideale, suprafață plană perpendiculară |
| Unghi de măsurare | ~15° | obiectele oblice pot reflecta ecoul în altă direcție |
| Durată impuls TRIG | 10 µs | minim necesar pentru declanșare |
| Frecvență maximă de citire | ~20 Hz | se așteaptă stingerea ecourilor reziduale |
8Calculator de distanță
Modificați durata ecoului și temperatura aerului. Observați diferența dintre
formula exactă și cea simplificată (t/58) - la distanțe mari și temperaturi extreme,
eroarea devine semnificativă.
const uint8_t PIN_TRIG = 9;
const uint8_t PIN_ECHO = 10;
float vitezaSunet(float tempC) {
return (331.3 + 0.606 * tempC) / 10000.0; // cm/us
}
float masoaraDistanta(float tempC) {
digitalWrite(PIN_TRIG, LOW);
delayMicroseconds(2);
digitalWrite(PIN_TRIG, HIGH);
delayMicroseconds(10); // impulsul de declansare
digitalWrite(PIN_TRIG, LOW);
unsigned long durata = pulseIn(PIN_ECHO, HIGH, 30000UL); // limita 30 ms
if (durata == 0) return -1.0; // fara ecou
return (durata * vitezaSunet(tempC)) / 2.0; // dus-intors -> impartit la 2
}
void setup() {
Serial.begin(9600);
pinMode(PIN_TRIG, OUTPUT);
pinMode(PIN_ECHO, INPUT);
pinMode(3, OUTPUT);
}
void loop() {
float d = masoaraDistanta(20.0);
if (d < 0) {
Serial.println("Fara ecou - obiect prea departe");
digitalWrite(3, LOW);
} else {
Serial.print("Distanta = "); Serial.print(d, 1); Serial.println(" cm");
digitalWrite(3, d < 20.0); // avertizare sub 20 cm
}
delay(100); // evita interferenta intre masuratori
}
9Integrarea aplicațiilor
Un program care face mai multe lucruri simultan nu poate folosi delay(). Structura
recomandată combină temporizarea neblocantă cu un automat de stări:
| Modul | Perioadă | Metodă |
|---|---|---|
| Citire senzor DHT | 2000 ms | millis() neblocant |
| Măsurare distanță | 100 ms | millis() neblocant |
| Comandă motor | la fiecare iterație | direct în loop() |
| Buton de oprire | imediat | întrerupere (Lab. 3) |
| Transmisie radio | 500 ms | millis() neblocant |
/* Cele patru sarcini sunt functiile scrise la aplicatiile de mai sus.
Aici sunt lasate goale, ca sa se vada limpede doar scheletul: */
void citesteDistanta() { /* aplicatia 4 */ }
void citesteTemperatura() { /* aplicatia 1 */ }
void trimiteTelemetrie() { /* aplicatia 3 */ }
void actualizeazaMotorul(){ /* aplicatia 2 */ }
void opresteMotorul() { /* opreste PWM-ul motorului */ }
unsigned long tDHT = 0, tDist = 0, tRadio = 0;
const unsigned long P_DHT = 2000, P_DIST = 100, P_RADIO = 500;
volatile bool oprireUrgenta = false;
void butonOprire() { oprireUrgenta = true; } // ISR foarte scurt
void setup() {
attachInterrupt(digitalPinToInterrupt(2), butonOprire, FALLING);
}
void loop() {
unsigned long acum = millis();
if (oprireUrgenta) { // are prioritate maxima
opresteMotorul();
return;
}
if (acum - tDist >= P_DIST) {
tDist = acum;
citesteDistanta();
}
if (acum - tDHT >= P_DHT) {
tDHT = acum;
citesteTemperatura();
}
if (acum - tRadio >= P_RADIO) {
tRadio = acum;
trimiteTelemetrie();
}
actualizeazaMotorul(); // la fiecare iteratie
}
10Simulator: automat de stări
Un robot care evită obstacole nu poate fi scris cu delay(): ar rămâne
orb exact când are nevoie de senzor. Urmăriți cum automatul de stări rezolvă problema, comutând între
stări în funcție de distanță și de timp.
loop()
senzorul este citit și starea este reevaluată. Nu există niciun moment în care robotul „nu vede".
Cu delay(600) în locul stării INAPOI, robotul ar fi complet orb timp de 0,6 secunde.11Montaj interactiv
Ultimele două montaje pun laolaltă tot ce ați învățat: citirea unui senzor, decizia, comanda unei ieșiri și execuția mai multor sarcini în paralel, fără să blocați programul.
delay() așa ceva este imposibil - o pauză de o secundă ar opri și clipirea rapidă. Tiparul millis() este baza oricărui program serios de microcontroler.12Sarcini de lucru
- Realizați stația meteo și afișați temperatura, umiditatea și indicele de căldură pe serial.
- Adăugați înregistrarea valorilor minime și maxime de la pornire.
- Montați puntea H și comandați motorul cu turație și sens variabile de la potențiometru.
- Determinați experimental factorul de umplere minim la care motorul începe să se rotească.
- Măsurați curentul absorbit de motor la diferite valori PWM și corelați-l cu turația.
- Realizați legătura radio între două plăci și transmiteți datele stației meteo.
- Detectați și afișați pachetele pierdute, folosind contorul din structură.
- Implementați măsurarea distanței cu compensare de temperatură de la senzorul DHT.
- Combinați două aplicații: senzorul de distanță oprește motorul la detectarea unui obstacol.
- Rescrieți un program cu
delay()sub formă de automat de stări și comparați reactivitatea.
13Proiect final
- două motoare comandate prin punte H, cu turație reglată prin PWM (Lab. 4)
- senzor HC-SR04 pentru detectarea obstacolelor (Lab. 5, Lab. 7)
- buton de pornire/oprire de urgență tratat prin întrerupere (Lab. 3)
- LED-uri de stare comandate prin acces direct la regiștri (Lab. 2)
- telemetrie transmisă pe serial sau radio (Lab. 6, Lab. 7)
- structură de automat de stări, fără niciun
delay()în bucla principală
Documentați proiectul cu schema electrică, codul comentat și un tabel cu rezultatele testelor: distanța de oprire în funcție de viteză, pentru cel puțin trei valori PWM diferite.