ublo
bogdan's (micro)blog

bogdan » arduino: termometru

09:21 am on Nov 7, 2018 | #more | tags:

în urma rugăminții profesorului Stamatin, de câțiva ani țin un curs la Facultatea de Fizică din Măgurele. dacă inițial numele «modelare și simulare» ascundea ecuații matematice și metode numerice, recent am înlocuit diferențialele cu Arduino, în ovațiile celor câtorva studenți care frecventează cursul. dotările limitate m-au făcut să devin creativ cu materialele de curs, iar rezultatul mi s-a părut suficient de interesant pentru a-l reproduce aici. așa că:

temperatura este una dintre mărimile fizice pe care o conștientizezi imediat: e prea frig afară, e prea fierbinte supa, oare ai febră? pe lângă conștientizarea acestei mărimi, este foarte simplu să achiziționezi un termometru cu care să poți cuantifica aceste senzații. dar unde e farmecul dacă nu e făcut de tine și nu poți înregistra periodic datele pentru a urmări procesele în timp real? în cele ce urmează îți voi prezenta o variantă de termometru pe care o poți realiza cu Arduino.

termistor

un termistor este un dispozitiv electronic în care temperatura modifică modul în care conduce curentul electric: dacă temperatura crește, rezistența pe care acesta o opune trecerii curentului electric se micșorează și invers. un Arduino nu poate măsura rezistența direct. dispozitivele cele mai potrivite din interiorul acestuia pentru o astfel de operație sunt convertoarele analog-digitale, care transformă o tensiune într-un număr. mai exact, pentru fiecare 0,004882V aplicați la intrare, Arduino va înregistra câte o unitate, în așa fel încât pentru 0V va înregistra 0, iar pentru 5V numărul 1023, variația fiind liniară (ex. 1,5V → 307, 3,3V → 676). acest lucru nu te ajută prea mult direct, mărimea care variază cu temperatura fiind rezistența dispozitivului. ajutorul vine din partea legii lui Ohm:

$$U = R_{termistor} I$$

unde:
U este tensiunea electrică la bornele termistorului, în volți (V),
Rtermistor este rezistența acestuia, în ohmi (Ω), iar
I este curentul care trece prin circuit, în amperi (A).

Arduino pune prin conectorii săi mai multe tensiuni pe care le poți utiliza ca referință: 3,3V (3V3), 5V sau Vin. cum Arduino poate măsura tensiuni între 0 și 5V, 3,3V e o tensiune prea mică față de capătul intervalului, pierzând astfel din precizie, Vin poate fi cu mult peste cei 5V și are și dezavantajul că nu este stabilă, rămânând astfel opțiunea 5V.

notă
3,3V poate fi folosit destul de ușor, conectând un fir între terminalul AREF și 3,3V, stabilind în acest fel capătul intervalului la valoarea 3,3V. acest lucru poate fi făcut cu orice sursă de tensiune, cuprinsă între 0 și 5V. trebuie să ții cont însă, că valoarea unei unități se modifică corespunzător. în cazul 3,3V, o unitate va însemna 0,003222V (ex. 1,5V → 465, 3,3V → 1023).

alegând U = 5V, în legea lui Ohm rămân totuși două necunoscute, rezistența termistorului și curentul prin circuit. pentru curent însă putem să aplicăm încă o dată legea lui Ohm, introducând în circuit un rezistor cu valoare cunoscută, în serie cu termistorul. tensiunea la bornele acestui rezistor de referință este direct proporțională cu curentul prin circuit, tensiune pe care o poți măsura direct folosind Arduino.

Simple Thermometer Animation

fie Rreferință valoarea în ohmi a rezistorului de referință și cu Ureferință valoarea măsurată cu ajutorul Arduino:

$$\begin{cases}5V = (R_{termistor} + R_{referinta}) I \\ U_{referinta} = R_{referinta} I\end{cases} \Rightarrow \\ R_{termistor} = R_{referinta} (\frac{5V}{U_{referinta}} – 1)$$

dependența de temperatură a rezistenței termistorului este dată de ecuația Steinhart-Hart, puțin prea complexă pentru nevoi obișnuite. drept urmare îți recomand să folosești ecuația beta, o formă simplificată a ecuației anterioare, dar utilizată de producători în caracterizarea termistorilor comercializați:

$$\beta = \frac{ln(R_{termistor}) – ln(R_{25})}{\frac{1}{T} – \frac{1}{T_{25}}}$$

ecuația anterioară descrie dependența de temperatura absolută T, în Kelvin, a rezistenței termistorului Rtermistor, în ohmi, folosind trei constante: R25, în ohmi, care este rezistența termistorului la 25°C – de altfel și valoarea sub care termistorul se comercializează, T25, în Kelvin, fiind temperatura absolută care corespunde 25°C, adică 298,15K și β care este o constantă proprie fiecărui termistor și care se măsoară tot în Kelvin.

ecuația anterioară poate fi aranjată într-o formă mai prietenoasă pentru calculul direct al temperaturii, cu mențiunea că temperatura obținută se măsoară în Kelvin, astfel:

$$T = \frac{1}{\frac{1}{\beta}ln(\frac{R_{termistor}}{R_{25}}) + \frac{1}{T_{25}}}$$

următorul pas în rezolvarea problemei este identificarea constantelor lipsă. pentru a determina facil aceste constante, îți recomand să achiziționezi termistorul dintr-o sursă care pune la dispoziție și datele de catalog. la curs am ales un termistor cu valoarea R25 de 100KΩ. raționamentul e simplu: 100KΩ deoarece puterea disipată prin trecerea curentului electric prin termistor nu-l va încălzi suficient pentru a afecta măsurătorile, iar în foaia de catalog vei găsi direct valoarea lui β. pentru termistorul pe care l-am ales, β=4600K.

Termistor Beta Characteristic

notă
pentru rezistorul de referință am ales o valoare egală cu R25. raționamentul e simplu, atunci când rezistenețele sunt egale, tensiunea în punctul median este jumătate din tensiunea de alimentare, plasând astfel temperatura de 25°C în centrul scalei. din cauza neliniarității funcției de conversie, îmi convine să am această valoare situată central deoarece este o valoare de interes pentru activitatea umană și măsurătorile din vecinătatea ei vor avea o precizie mai mare. în general, e bine să aleg rezistorul de referință în așa fel încât temperatura medie a intervalului de interes să corespundă jumătății tensiunii de alimentare.

programare

în acest moment ai toate informațiile necesare pentru a putea scrie un mic program Arduino care să citească temperatura cu ajutorul unui termistor conectat în configurația descrisă mai sus. pentru început, orice program Arduino conține o funcție setup și o funcție loop.

conținutul funcției setup este rulat de Arduino atunci când acesta este inițializat, fie după reset, fie atunci când este alimentat, o singură dată. în această funcție se trec de obicei instrucțiuni pentru a inițializa perifericele Arduino. în cazul de față, convertorul analog-digital care poate măsura tensiuni este inițializat automat.

funcția loop este cea care ne interesează aproape întotdeauna. proprietatea care o face interesantă este că atunci când lista de instrucțiuni pe care le conține a fost epuizată, loop se va reseta și o va lua de la început. Arduino va face acest lucru până când este apăsat butonul reset, se încarcă un program nou sau alimentarea plăcuței este întreruptă.

în fragmentul de cod de mai jos apare și funcția analogRead cu parametrul A0. această funcție primește ca parametru eticheta unuia dintre pinii analogici ai Arduino, de la A0 la A5 și întoarce un număr întreg cuprins între 0 și 1023, număr care este proporțional cu tensiunea aplicată intrării A0. având în vedere că numărul întors este întreg, pentru a forța zecimale în calculele realizate de Arduino, am ales să reprezint toate numerele în format zecimal (”cu virgulă”).

cuvântul cheie float din fața etichetei U_referinta anunță Arduino să rezerve memorie în care să stocheze un număr zecimal care poate fi apelat întotdeauna folosind eticheta U_referinta. același lucru se aplică și pentru R_termistor, T și t. vei observa că eticheta acestor fragmente de memorie ține cont de tipul literelor (mici sau mari), astfel încât T și t vor face referire la fragmente separate, deci valori diferite.

am mai utilizat încă două funcții, log, care corespunde logaritmului natural, luând ca parametru un număr zecimal și întorcând tot un număr zecimal și delay, care suspendă execuția listei de instrucțiuni pentru numărul de milisecunde furnizate drept parametru. dacă nu folosești această funcție, operațiile vor fi efectuate extrem de rapid, furnizând o cantitate mare de date, oarecum irelevantă, mai ales datorită vitezei relativ mici de variație a rezistenței termistorului cu temperatura.

void setup() {
}

void loop() {
  // măsor tensiunea în volți înmulțind valoarea citită prin
  // conectorul A0 cu factorul 0.004882V (= 5V / 1024)
  float U_referinta = 0.004882 * analogRead (A0);
  // din U_referință și valoarea lui R_referință = 100k ohmi
  // calculez R_termistor
  float R_termistor = 100000.0 * (5.0 / U_referinta - 1.0);
  // folosim ecuația temperaturii, în care beta = 4600K și
  // R_25 = 100k ohmi, T_25 = 298.15K și obținem temperatura în
  // Kelvin
  float T = 1.0 /
  (1.0 / 4600.0 * log (R_termistor / 100000.0) + 1.0 / 298.15);
  // pentru a afla temperatura în grade Celsius, scădem din
  // temperatura în Kelvin 273.15
  float t = T - 273.15;
  // fac o pauză de 1 secundă, după care repet instrucțiunile
  delay (1000);
}

micul program de mai sus poate fi compilat și încărcat pe plăcuța ta Arduino folosind Arduino IDE. conectezi Arduino prin cablul USB la calculator și aștepți ca sistemul de operare să instaleze automat uneltele pentru conectare. apoi deschizi aplicația Arduino și vei alege utilizând meniul Tools → Boards → modelul de Arduino pe care îl ai și folosind meniul Tools → Port → portul la care ai conectat plăcuța. te prinzi ușor despre ce port e vorba. dacă nu, le poți încerca fără probleme pe toate, până reușești.

în fereastra care s-a deschis, copiezi codul de mai sus, sau mai bine, îți recomand să-l scrii de mână. poți sări peste liniile care încep cu // și sunt colorate cu albastru. ele reprezintă comentarii și nu influențează programul. după ce ai terminat, vei apăsa butonul Verify, așteptând ca în bara de stare a ferestrei să apară cuvintele Done compiling.

Arduino - Verify - Upload

în situația în care au apărut erori, verifică în ordine următoarele lucruri:

  • ai pus ; (punct și virgulă) după fiecare instrucțiune? din experiența cu studenții, e de departe cea mai frecvent întâlnită eroare;
  • fiecare paranteză deschisă, fie că este paranteză rotundă sau acoladă, trebuie să se și închidă la un moment dat, fix ca la matematică, în ordinea inversă deschiderii lor. adică {…(…)…} este corect, în timp ce sunt greșite: {…(…}…), {…(…)…, {…)…}
  • felul literelor contează mult, așa că verifică dacă ai scris cu litere mari și mici ca în exemplul de mai sus; etichetele, cum sunt U_referinta, R_termistor, T și t le poți modifica după plac, cu singura mențiune să le modifici pe peste tot;
  • cu excepția liniilor cu comentarii, în rest nu ai voie să folosești diacritice; știu, nu e așa comun, dar am întâlnit și eroarea asta;

Arduino Done Compiling

ultimul pas este cel de încărcare al programului pe plăcuța Arduino, apăsând butonul Upload. în caz de erori, cele mai întâlnite sunt:

  • nu ai selectat corect portul la care este conectat Arduino;
  • nu ai selectat corect tipul de plăcuță Arduino; pentru a te asigura, tipul plăcuței este scris pe ea, nu ai cum să greșești;
  • portul USB la care ai conectat plăcuța nu funcționează corect și va trebui să alegi altul;
  • cablul USB nu este de calitate și va trebui să-l schimbi sau să folosești unul mai scurt;

note

  • în jargonul programatorilor, loop și setup poartă denumirea de proceduri, pentru că nu returnează nimic. cuvântul cheie void care alcătuiește definiția unei funcții specifică fix acest lucru: că respectiva bucată de cod întoarce ”vid” (nimic). prefer să folosesc denumirea de funcție, care provine din matematică, pentru a simplifica asimilarea noțiunilor.
  • în teorie funcția log are nevoie de o bibliotecă de funcții să funcționeze. din fericire, Arduino știe să o adauge implicit, așa că micul program va funcționa fără probleme așa cum este scris.
  • în ceea ce privește tipul de variabile pe care lucrează implicit log, care se numește double, pentru majoritatea plăcuțelor Arduino, acesta este doar un sinonim pentru float. chiar dacă nu ar fi așa, Arduino se ocupă de conversia implicită între cele două formate.
  • spațiile nu sunt relevante, însă ele asigură lizibilitate programului scris, așa că dacă ți-e lene, le poți omite.

lcd

până acum, Arduino știe ce temperatură măsoară, însă o ține pentru el. pentru a o putea afișa, are nevoie de un dispozitiv extern, cum ar fi un ecran LCD cu 2 rânduri a câte 16 caractere, foarte des întâlnit (și ieftin). în plus, mai ai nevoie de un mic potențiometru semiregrabil necesar în ajustarea contrastului, cu valoarea de 10KΩ, o rezistență de 220 de Ω, câteva fire de conexiune mai lungi, colorate și o plăcuță pentru experimente (breadboard). tot ce ai de făcut în continuare pe partea fizică este să replici construcția din desenul următor, după ce ai deconectat Arduino de la portul USB:

Arduino Thermometer with LCD

sfatul meu este să începi cu firele roșii și negre (alimentarea), plasând apoi rezistența de 100KΩ, cea de 220 de Ω, termistorul și potențiometrul semiregrabil pe plăcuța pentru experimente, urmate de firele mov, galben, portocaliu și roz, iar într-un final de cele verzi. verifici de două ori așezarea firelor și conexiunile, ții degetele încrucișate și conectezi din nou Arduino la portul USB. nu-ți face griji, portul USB este protejat în caz de probleme, așa că nu ai ce strica la calculator. un semn bun, că totul merge, îl reprezintă luminarea ecranului LCD. ajustând potențiometrul semiregrabil, ar trebui să vezi la unul dintre capete 2 rânduri cu câte 16 pătrățele negre alcătuite din pixeli, pe ecran. dacă nu funcționează, verifică din nou conexiunile.

vei modifica acum micul program de mai sus pentru a include ecranul LCD folosind biblioteca inclusă în aplicația Arduino, LiquidCrystal.h. aceasta definește un tip de obiect LiquidCrystal care poate fi folosit pentru a controla ecranul LCD. pentru început, vei ințializa obiectul dându-i o denumire și specificând conexiunile către Arduino. pentru a inițializa ecranul, vei adăuga instrucțiunea de inițializare a acestuia în funcția setup: lcd.begin. inițializarea se face specificând dimensiunile ecranului. Arduino știe prin intermediul LiquidCrystal.h să comunice cu ecrane de mai multe dimensiuni.

// am nevoie de o bibliotecă care știe să comunice cu ecranul LCD
#include <LiquidCrystal.h>
// inițializez un obiect de tip LiquidCrystal pe care îl denumesc
// lcd, căruia îi spun la ce conectori Arduino am conectat firele
// 12 = galben, 11 = portocaliu, 5, 4, 3, 2 = cele 4 fire verzi
LiquidCrystal lcd (12, 11, 5, 4, 3, 2);

void setup() {
  // inițializez ecranul și îi spun că are 2 rânduri cu 16 caractere
  lcd.begin (2, 16);
}

după ce ai calculat temperatura, vei curăța ecranul pentru a te asigura că nu e nimic pe el folosind lcd.clear. vei muta cursorul pe coloana 0, linia 0 (colțul stânga-sus) folosind lcd.setCursor, cu parametri coloană (de la 0 la 15) urmat de linie (de la 0 la 1), după care vei tipări diferite lucruri, în ordine, folosind lcd.print. lcd.print poate primi ca parametri șiruri de caractere delimitate de ghilimele, numere zecimale sau întregi, dar și caractere speciale, prin codul lor, cum este simbolul °, dar pe care trebuie să-l diferențiezi de un număr întreg obișnuit, folosind sintaxa (char). această operație se numește conversia tipului (cast în engleză) și îi spune Arduino să trateze numărul 223 ca un caracter (char).

notă
lcd este o etichetă pe care am ales-o eu în vederea accesării obiectului LiquidCrystal. dacă ai altă preferință, cum ar fi ecran, o poți folosi fără probleme. trebuie să ții cont că funcțiile apelate se vor modifica corespunzător: lcd.begin devine ecran.begin, lcd.clear devine ecran.clear, lcd.setCursor devine ecran.setCursor și lcd.print devine ecran.print. acest tip de funcții, care depind de obiectul folosit, în jargonul programatorilor se numesc metode și se folosesc de starea internă a obiectului de care sunt anexate pentru a-și îndeplini rolul. fără inițializarea unui astfel de obiect, ele nu pot fi folosite, așa că să nu uiți bucata anterioară funcției setup în care alegem o etichetă pentru un obiect de tip LiquidCrystal.

void loop() {
  // ...
  // bucata de până aici rămâne neschimbată
  float t = T - 273.15;
  // dacă e ceva scris pe ecranul LCD, îl șterg
  lcd.clear ();
  // îi spun ecranului LCD că vreau să scrie începând cu colțul stânga-sus
  // primul 0 îl reprezintă coloana (0-15), al doilea rândul (0-1)
  lcd.setCursor (0,0);
  // pornind de la cursor, afiseaza T = 
  lcd.print ("T = ");
  // apoi temperatura în grade Celsius
  lcd.print (t);
  // urmată de simbolul grad
  lcd.print ((char) 223);
  // și apoi de litera C
  lcd.print ("C");
  // fac o pauză de 1 secundă, după care repet instrucțiunile
  delay (1000);
}

după încărcarea programului pe plăcuța Arduino, singurul lucru pe care îl mai ai de făcut este să ajustezi contrastul în așa fel încât literele și cifrele să fie lizibile.

bogdan » arduino: lucruri necesare

05:24 pm on Oct 31, 2018 | #more | tags:

am descoperit Arduino în 2009, într-o reclamă Google. ce m-a uimit e că pe o mică plăcuță am regăsit ceva asemănător primului calculator pe care l-am văzut în viața mea, CIP-03, o clonă de ZX-Spectrum. un calculator care mi-a gravat în minte așteptările pe care trebuie să le am de la o mașină de felul ăsta: să pot să o programez și să interacționeze cu lumea din exterior. Arduino le face pe-amândouă, lucru pentru care l-am inclus în cursul meu de modelare și simulare pe care-l țin periodic la Facultatea de Fizică. recent, am primit la curs întrebarea: ce-ar însemna un set minim cu care să pot experimenta cu Arduino? așa că răspunsul vine în continuare.

arduino

Arduino UNO R3 – prefer varianta cu USB-type-B (conector USB pentru imprimantă) și AtMega328-PU (cip cu piciorușe într-un soclu); raționamentul e simplu: așa cum am văzut în propriul buzunar, când faci experimente cu Arduino poți să-l strici; un cip separat e numai o fracțiune din preț și e majoritar componenta care se strică cel mai ușor; alt motiv este că în timp, vrei să incluzi Arduino în proiecte mai mici și mai statice, iar un Arduino cu soclu îți permite să programezi mai multe cipuri fără bătăi de cap. când ai avansat, poți trece la variantele cu cip miniatură lipit, în diferite forme, dar primul Arduino ar trebui să fie UNO R3 clasic; (29 de lei, 30 de lei, 146 de lei)
LED-uri – pentru că primul lucru pe care-l vei face cu el e să aprinzi luminițe; e simplu și oferă satisfacții imediate; recomand LED-uri de 5mm, cât mai multe, cât mai colorate (49 de lei); câteva IR nu strică (3,5 lei / 5 bucăți);
fire de conexiune – Arduino e făcut să folosească fire de conexiune; dacă LED-urile se pot pur și simplu ”înfige” în sloturile Arduino, celelalte componente sunt mult mai greu de conectat așa că două seturi de fire de conexiune (un set are 10 fire), unul cu pini la ambele capete (”tată-tată”), celălalt cu pini și socluri (”mamă-tată”) sunt valoroase (3,5 lei / 10 bucăți / 15 cm / tată-tată și 4,5 lei / 10 bucăți / 15 cm / tată-mamă);
placă de conexiune (breadboard) – pentru a putea crea mici circuite ai nevoie de o placă de conexiune; recomand una medie (half-size), cu linii pentru alimentare – acestea sunt linii perpendiculare pe rândurile conectate ale plăcii, care pot fi folosite pentru alimentarea circuitului (5 lei, 5 lei, 33 de lei (calitate mai bună));
ecran LCD – după ce ai aprins LED-uri și te-ai jucat puțin cu interfața serială, vei fi uimit că poți scrie mesaje pe un mic ecran LCD; îl recomand pe cel ieftin, cu conexiune paralelă; necesită în plus un potențiometru de 10KΩ și destul de multe fire, dar are avantajul că e ieftin (11 lei, 19 lei, 35 de lei); o variantă mai bună e versiunea I2C a acestuia, dar ceva mai scumpă (18 lei, 29 de lei, 76 de lei);
butoane – știi să afișezi lucruri, deci să obții informație din Arduino; e momentul să și introduci și ce e cel mai simplu decât să conectezi butoane cu contact momentan la el (push-buttons); un simplu meniu are nevoie de minimum 3 butoane: sus/jos pentru a naviga în el și un buton pentru a selecta opțiunea (4,5 lei / 3 bucăți, 6 lei / 3 bucăți);
potențiometre – când butoanele nu te mulțumesc, vei vrea să folosești un potențiometru; la un Arduino poți conecta între 1 și 6 (9 lei / 6 bucăți, 15 lei / 3 bucăți);
rezistori – Arduino limitează curentul prin terminalele sale, însă de multe ori ai nevoie fie să limitezi curentul mai mult, fie să le folosești pentru a forța anumite intrări într-o poziție (pull-up, pull-down) fie pentru a limita tensiunile analogice; valorile pe care le recomand sunt 470Ω (0,6 lei), 1KΩ (0,6 lei), 4.7KΩ (0,6 lei), 10KΩ (0,6 lei), 100KΩ (0,6 lei) și 1MΩ (0,6 lei). câte 10 din fiecare, pentru că oricum sunt ieftine;
punte H – una dintre aplicațiile cele mai spectaculoase ale Arduino este aceea de a face lucrurile să se miște; iar pentru a putea comanda motoare folosind Arduino, ai nevoie de o punte H; de preferat dublă pentru că e foarte ușor să controlezi mișcarea unui robot utilizând două motoare (10 lei);
tranzistori MOS-FET – de la LED-uri mici la LED-uri mai mari e un singur pas, așa că MOS-FET-urile sunt extrem de utile pentru controlul consumatorilor mari de curent; fie ca vrei să creezi o mini-seră încălzită sau să controlezi un element Peltier pentru a-ți răci băutura preferată, un MOS-FET e ideal; numărul minim recomandat este 2 (5 lei / 2 bucăți);
relee – când vezi că poți controla atâtea lucruri în jurul tău, te gândești cum poți trece la nivelul următor: poate să aprinzi o veioză? un releu te ajută să separi Arduino de tensiunile periculoase (priză, de cele mai multe ori). 2 sunt suficiente, de preferat cu tranzistorii și diodele necesare pentru control; dacă nu, se mai trec în listă câte un tranzistor și o diodă de curent mic pentru fiecare releu (7 lei / 2 bucăți, 19 lei / 2 bucăți);
conector baterie – la un moment dat proiectele vor deveni independente de interfața USB; în această situație, un conector de la o baterie de 9V la borna de alimentare a Arduino devine esențial (6 lei); o variantă mai bună, este obținearea unei cutii pentru baterii AA minimum 4 (3,5 lei), deși recomand 6 (5 lei) cu conector pentru alimentare – deoarece Arduino consumă destul de mult curent, iar o baterie de 9V are o capacitate relativ mică;

câteva tipuri de senzori

fotorezistori – așa cum îi spune numele, un fotorezistor își variază rezistența cu lumina incidentă: cu cât e mai multă lumină, cu atât rezistența e mai mică; poate fi extrem de util pe post de ”ochi” pentru un mic proiect Arduino; doi sunt utili (3,9 lei / 2 bucăți, 4 lei / 2 bucăți);
termistor – pe lângă lumină, temperatura e o altă mărime fizică fundamentală care poate fi convertită ușor în rezistență; un termistor este o rezistență care crește atunci când e frig și scade când e cald; recomand un termistor cu valoarea de 100KΩ; unul e suficient (3 lei);
microfon – poate nu la fel de facil de utilizat, însă cu siguranță spectaculos, un senzor util este cel pentru sunet; recomand un microfon cu electret și amplificator integrat, pentru a putea trimite semnalul acestuia direct, către un Arduino; util în proiectele în care vrei ca Arduino să ”simtă” muzica (36 de lei);
senzor de distanță – aici sunt mai multe opțiuni: optic sau cu ultrasunete; îl recomand pe cel din urmă, având o rază mai mare de acțiune, cele optice putând fi ușor replicate cu simple leduri (20 de lei, 25 de lei, 29 de lei);

câteva tipuri de elemente de control

difuzor / buzzer – după semnale optice, a doua metodă de a obťine feedback de la Arduino sunt sunetele; conectat direct la unul dintre piciorușele Arduino, un mic difuzor poate fi elementul care poate face proiectul tău mai interesant (2 lei, 6 lei, 10 lei);
servomotoare – sunt mici motoare care au inclus un circuit de feedback și cu ajutorul cărora poți interacționa prin mișcări precise cu lumea reală; recomand două, în special utile pentru a poziționa un senzor (ultrasonic sau optic) cu două grade de libertate (pe suprafața unei sfere) pentru, spre exemplu, un radar 3D (40 de lei / 2 bucăți, 46 de lei / 2 bucăți, 58 de lei / 2 bucăți);
motoare – sunt elementele care asigură autonomie unui robot controlat de Arduino; recomand două, cu reductor și roată inclusă (24 de lei / 2 bucăți);

câteva elemente esențiale, dar care pot fi achiziționate ulterior

fire de conexiune pentru breadboard (jumperi) – deși nu sunt necesare, sunt extrem de utile atunci când dezvolți circuite complexe folosind o placă pentru conexiuni; permit evitarea interferențelor parazite cauzate de firele prea lungi (15 lei, 25 de lei, 32 de lei);
un programator ICSP – am luat unul atunci când am stricat primul Arduino; e util pentru reprogramarea fără bătăi de cap a bootloader-ului pe cip și nu numai (60 de lei, 20 USD – include convertor USB-serial); poate fi simulat cu un alt Arduino;
un multimetru – nu neapărat necesar, este util în depanarea circuitelor și identificarea mai ușoară a problemelor: fie e un fir întrerupt, fie o tensiune nu e suficientă, fie un curent e prea mare; multimetrul identifică cu ușurință aceste probleme; ca să nu mai vorbim de rezistorii cu peliculă metalică pe care eu nu disting culorile (55 de lei, 61 de lei); poate fi emulat cu puțină îndemânare cu un Arduino;
convertor USB-serial – nu e esențial, dar incredibil de util pentru reprogramarea proiectelor care folosesc variante miniaturizate de Arduino (15 lei, 39 de lei, 75 de lei – include ICSP); poate fi emulată cu un Arduino;
o sursă coborâtoare/ridicătoare de tensiune – pentru proiectele mobile e o necesitate; Arduino conține un stabilizator de tensiune liniar, ieftin și robust, dar lipsit de eficiență; un astfel de circuit permite alimentarea de la acumulatori; recomand o variantă care să poată susține 5V la cel puțin 1.5A (30 de lei, 30 de lei);

raspberry pi

Raspberry PI 3 – e un mic calculator pe o plăcuță de mărimea unui card bancar; dacă Arduino e echivalentul unui ZX-Spectrum, Raspberry PI 3 e un fel de Pentium 3; adică destul de puternic pentru a vedea filme și a naviga pe internet; mai mult, are 4 porturi USB pentru periferice normale, o mulțime de spațiu de stocare și interfețe de rețea: bluetooth, wi-fi și ethernet; altfel, face cam ce face Arduino, dar cu un sistem de operare obișnuit, care suportă compilator C, limbaje de scripting (PHP, Ruby, Perl, JS) și servere http și ssh (209 lei, 245 de lei, 259 de lei);
RPI-UNO-HAT – produs de echipa din spatele watterott.com, RPI-UNO-HAT este o extensie a Raspberry PI care conține un Arduino împreună cu circuitele necesare interfațării; deși poate fi emulat cu un Arduino și câteva componente discrete, recomand varianta asta pentru simpliatatea în utilizare; poți astfel să construiești lucruri complexe, conectate la internet; RPI-UNO-HAT necesită un mic circuit pentru alimentarea Raspberry PI prin conectorul comun, care în această situație trece în lista de componente obligatorii (16 EUR);

software

Arduino – așa cum îi spune numele e complementul software al plăcuței; cu ajutorul acestuia poți programa o mulțime de plăcuțe compatibile cu Arduino (download: windows, linux, mac os x);
GNU Octave – dacă ești familiarizat cu MatLab, GNU Octave e o variantă open-source a acestuia; altfel, imaginează-ți-l ca pe un calculator foarte avansat care face în primul rând operații cu matrice (download: windows, linux, mac os x); sunt utile pachetele suplimentare io (download) și instrument-control (download);
native script – este o extensie de node.js care permite dezvoltarea de aplicații mobile; foarte util atunci când vrei să conectezi proiectul tăubla telefon (ghid instalare);
Microsoft Visual Studio Code – de departe cel mai bun editor text pentru programatori; chiar dacă e făcut de Microsoft, e open source, rulează cam pe orice platformă și e bun! (download);
Git/GitHub – atât clientul desktop cât și cel de consolă, împreună cu un cont gratuit, pentru a salva și a împărtăși cu toată lumea experimentele tale (download);

bogdan » how to build a maze

01:42 pm on Oct 7, 2018 | #more | tags:

in this post i’ll use C to construct a maze, that will be later used for my swarm experiments.

i’ll be using a graph-model for the maze: each vertex is a room from the maze, which is connected by edges to other rooms. in a 2-D classic maze of size m×n, with m spanning from top to bottom and n spanning from left to right, each room has at most 4 neighbors. this means each vertex in my graph will have at most 4 edges: top, bottom, left and right. the total number of edges is m&times(n-1) for vertical edges (top, bottom) and (m-1)×n for horizontal edges (left, right). in order to create a maze, in the previously described graph, i’ll choose using a version of Dijkstra algorithm a maximum spanning tree. to make the maze random, i’ll mark each edge with a random weight and then run the maximum spanning tree algorithm.

maze model

i’ll be using matrices as data structures. this means that each vertex will be labeled with a pair (i,j) with i from 0 to m-1 and j from 0 to n-1. as the graph is not oriented, i’ll have:

  • if (i,j) is linked to (i,j+1), than i’ll store in my matrix, on position (i,j) the weight of the edge [(i,j),(i,j+1)];
  • if (i,j) is linked to (i+1,j), than i’ll store in my matrix, on position (i,j+n) the weight of the edge [(i,j),(i+1,j)];
  • no need to store the [(i,j),(i,j-1)] edge as it’s equivalent to [(i,(j-1)),(i,(j-1)+1)];
  • also, no need to store connections at the lower and right edge of the maze;

maze storage

the number of required edges’ weights can be held in a m×(2×n-1)-n vector, corresponding to an incomplete m×(2×n-1) matrix.

building the weight matrix

this step is quite easy. just loop over all m×(2×n-1)-n and assign them a random value between 1 and 255. i’ll be using 0 to represent edges that are missing.

/**
 * the function initializes the weights matrix as described above;
 * @param m (int) the dimension of the maze on top-bottom axis;
 * @param n (int) the dimension of the maze on left-right axis;
 * @returns: an uint8_t matrix, of size (m-1)*2*(n-1) filled with random data;
 */
uint8_t * maze_weight_init (int m, int n) {
        /** @var maze the weight matrix */
        uint8_t * maze;
        /** @var c is  a variable index */
        int c;

        /** i reserve the space in the computer memory for the matrix */
        maze = (uint8_t *) malloc ((m * (2 * n - 1) - n) * sizeof (uint8_t));
        /** if memory reservation is successful, i'm filling the matrix */
        if (maze != NULL)
                for (c = 0; c < m * (2 * n - 1) - n; c++)
                        *(maze + c) = (uint8_t) ((1 + rand ()) % 256);

        return maze;
}

tip:
the matrix is stored as a single vector, meaning a cell on the position (i,j) can be accessed by finding as the (i×cols+j)-th component of the vector. this map is bijective, meaning that the i-th component of the vector corresponds to position (i/cols, i%cols) in the matrix, where / is the integer division and % is the reminder operator.

sorting the weight matrix

the matrix is not useful like this. it needs to be sorted. i'm using a simple (inefficient) bubble sort algorithm. i'll take advantage of how i stored the matrix so i can simply arrange the associated vector. i'll create a vector into which i'll keep the association of indices.

bubble sort works like this: take the first weight from the vector, and if it's heavier that the one above, swap their places. this has to be repeated until the entire vector is sorted: this happens when there are no swaps on a swipe.

/** the function is sorting the associated vector V, returning a mapping of the
 * sorted indices, M, with V(i) being the original vector, while V(M(i)) is the sorted one
 * @param weights (uint8_t *) is the original vector
 * @param m (int) is the top-bottom size of the maze
 * @param n (int) is the left-right size of the maze
 * @returns: an int mapping vector between the sorted version and the original one
 */
int * maze_edges_sort (uint8_t * weights, int m, int n) {
        /** @var indices the vector containing the sorted mapping */
        int * indices;
        /**
         * @var c a variable index
         * @var t a temporary variable used in swapping two indices
         * @var o a temporary variable storing last index swapped
         * @var l, initially the length of the weights vector,
         *        after, the length of the unsorted vector
         */
        int c, t, o, l = m * (2 * n - 1) - n;

        /** initialize indices with an identity mapping i->i */
        indices = (int *) malloc (l * sizeof (int *));
        for (c = 0; c < l; c++)
                *(indices + c) = c;

        if (indices != NULL)
                /** while there's still some unsorted part of the vector */
                while (l > 0) {
                        /** o holds the last swapped value */
                        o = 0;
                        /** i'm looping until the end of the unsorted vector */
                        for (c = 1; c < l; c++) {
                                /** if the weights don't respect the sort order, swap them */
                                if (*(weights + *(indices + c - 1)) > *(weights + *(indices + c))) {
                                        /**
                                         * of course, i'm swapping indices in the mapping,
                                         * not real values, as the initial vector remains
                                         * unchanged, only the mapping function deviates from
                                         * identity map
                                         */
                                        t = *(indices + c - 1);
                                        *(indices + c - 1) = *(indices + c);
                                        *(indices + c) = t;
                                        /** store the last swapped index */
                                        o = c;
                                }
                        }
                        /** the length of the unsorted vector equals the last swapped index */
                        l = o;
                }

        return indices;
}

building the maximum spanning tree

this algorithm is more complex that the previous ones. for easier understanding, i need to explain some notions:

  • a graph is a collection of vertices and edges, each edge connecting two vertices; it can and usually has loops - meaning starting from one vertex, it can be reached again going edge-by-edge; like a city with ring-roads;
  • a tree is a graph with no loops; it means that starting with a vertex, and going edge-by-edge you'll never get to the same vertex again; like a computer network;
  • in a graph (including a tree), a sub-tree is a collection of vertices and edges from the initial graph that behave like a tree - there are no loops;

i'll start with an empty set of sub-trees from the original graph. this will be called a forest set. forests have at least one tree. at the end of the algorithm i'll have in this set a tree that will link all vertices from the original graph. this is called a maximum spanning tree.

with each sorted edge, lighter to heavier, i'll do the following:

  • if the edge is not connected to any sub-tree from my sub-tree set, i'll add the edge to the sub-tree set, coloring it with a new color;
  • if the edge has one vertex connected to a sub-tree in my sub-tree set, i'll add the edge to the sub-tree set, coloring it with the same color as the rest of the sub-tree;
  • if the edge has vertices on different sub-trees, than i'll add the edge and color both sub-trees, as well as the edge, with a common color. i'll use indexed colors, so i'll choose the minimum color;
  • if the edge has vertices on the same sub-tree, i'll skip this edge; in this situation, the edge is creating a loop in the tree, making it a graph;
/**
 * the procedure takes as parameters a weight matrix together with its size
 * and modifies in place the weights matrix, removing the unnecessary edges
 * an edge is kept, if the weight is strict positive, while an edge is
 * deleted, if its weight is zero.
 * @param weights (uint8_t *) is the weight vector
 * @param m (int) is the maze top-bottom size
 * @param n (int) is the maze left-right size
 */
void maze_init (uint8_t * weights, int m, int n) {
        /**
         * @var indices is the sorted mapping for the weights vector;
         * @var matrix is a vector-stored matrix that has 0 on (i,j)
         *         0 on (i,j) position, if the (i,j) vertex was not visited
         *         color>0 on (i,j) position, if the (i,j) vertex was visited
         *         and (i,j) is part of the "color" sub-tree
         */
        int * indices, * matrix;
        /**
         * @var c,d are variable indices;
         * @var row,col are the row and column indices in the @see matrix
         * @var n_row,n_col are the indices for the vertex connected to
         *        (row,col);
         * @var min_color is the minimum "color" when merging an existing
         *        sub-tree with a new edge. the edge can link two existing
         *        sub-trees, meaning i'll have to choose a single color for both
         * @var max_color is analog with @see min_color;
         * @var color is the current available color for new sub-trees;
         */
        int c, d, row, col, n_row, n_col, min_color, max_color, color = 1;

        /** reserve memory for the is-visited? matrix */
        matrix = (int *) malloc (m * n * sizeof (int));
        for (c = 0; c < m * n; c++)
                *(matrix + c) = 0;

        /** sort the graph weights */
        indices = maze_edges_sort (weights, m, n);

        /** loop through sorted edges */
        for (c = 0; c < m * (2 * n - 1) - n; c++) {
                /** get the edge first vertex label as (row, col) */
                row = *(indices + c) / (2 * n - 1);
                col = *(indices + c) % (2 * n - 1);

                /**
                 * based on how is stored, get the label for the
                 * second vertex associated with the edge
                 */
                if (col < n - 1) {
                        n_row = row;
                        n_col = col + 1;
                }
                else {
                        col = col - n + 1;
                        n_row = row + 1;
                        n_col = col;
                }

                /**
                 * check if the current edge can be added to the forest:
                 * the edge needs to fulfill all requirements:
                 * - the edge is not already part of a tree, meaning that both
                 *        vertices are the same non-zero color
                 */
                if (
                        (*(matrix + row * n + col) == *(matrix + n_row * n + n_col)) &&
                        *(matrix + row * n + col) > 0
                ) {
                        *(weights + *(indices + c)) = 0;
                        continue;
                }

                /**
                 * find the color of the new vertices, by getting minimum and
                 * maximum color. both can be zero, if we start a new sub-tree
                 */
                if (*(matrix + row * n + col) < *(matrix + n_row * n + n_col)) {
                        min_color = *(matrix + row * n + col);
                        max_color = *(matrix + n_row * n + n_col);
                }
                else {
                        min_color = *(matrix + n_row * n + n_col);
                        max_color = *(matrix + row * n + col);
                }

                if (min_color == 0) {
                        /**
                         * here, min = max = 0, this means we have a new sub-tree
                         * so i color it with the next available color
                         */
                        if (max_color == 0) {
                                *(matrix + row * n + col) =
                                *(matrix + n_row * n + n_col) = color ++;
                        }
                        /** here, the edge has an open end, the other is connected */
                        else {
                                *(matrix + row * n + col) =
                                *(matrix + n_row * n + n_col) = max_color;
                        }
                }
                else {
                        /**
                         * here min, max > 0, both different, this means that the
                         * edge is connecting two different color sub-trees, so i
                         * make both the same color (min color)
                         */
                        *(matrix + row * n + col) =
                        *(matrix + n_row * n + n_col) = min_color;

                        for (d = 0; d < m * n; d++)
                                if (*(matrix + d) == max_color)
                                        *(matrix + d) = min_color;
                }
        }

        /** remember to free the reserved memory */
        free (indices);
        free (matrix);
}

putting it all together

piece of cake. first, i'll have to initialize my weights. than, to build the maximum spanning tree. a good exercise is to display the maze on the screen using ASCII art.

#include  /** required for malloc, srand and rand functions */
#include  /** useful for printing stuff, with printf */
#include  /** i need the uint8_t definition */
#include  /** i need the time function */

/**
 * this is the normal, unix-style format for the program entrypoint
 * @param argc (int) is the number of arguments from the command line;
 * @param argv (char **) is an array of strings, containing the command line arguments;
 * @returns: an integer, zero if no error has occurred.
 */
int main (int argc, char ** argv) {
        /** @var weights (uint8_t *) my weights matrix */
        uint8_t * weights;
        /** @var m, n (int) the maze size */
        int m = 4, n = 4;

        /** make the random numbers random 🙂 */
        srand (time (NULL));

        /** initialize the weights matrix */
        weights = maze_weight_init (m, n);
        /** build a maximum spanning tree from the matrix */
        maze_init (weights, m, n);

        /** tell the operating system that there's no error */
        return 0;
}

bogdan » arduino: conectarea la octave

10:29 pm on Apr 9, 2018 | #more | tags:

în urma rugăminții profesorului Stamatin, de câțiva ani țin un curs la Facultatea de Fizică din Măgurele. dacă inițial numele «modelare și simulare» ascundea ecuații matematice și metode numerice, recent am înlocuit diferențialele cu Arduino, în ovațiile celor câtorva studenți care frecventează cursul. dotările limitate m-au făcut să devin creativ cu materialele de curs, iar rezultatul mi s-a părut suficient de interesant pentru a-l reproduce aici. așa că:

GNU Octave:
GNU Octave este o alternativa gratuită a MathWorks MatLab. ambele fac în mare același lucru pentru utilizatorul de rând: efectuează extrem de eficient operații matematice cu matrice. mai mult, în cele mai multe cazuri, codul scris pentru unul funcționează fără probleme în celălalt. GNU Octave poate fi descărcat de aici.

recomand varianta zip, pentru arhitectura potrivită calculatorului tău cu mențiunea că în cazul în care nu știi ce înseamnă arhitectură, poți alege cu încredere varianta care are în numele fișierului w32. fișierul descărcat este o arhivă de tip zip și necesită dezarhivare. dacă utilizezi Microsoft Windows 10, poți folosi cu încredere utilitarul integrat în Windows Explorer. vei obține un director care conține octave.bat, fișierul pe care trebuie să dai dublu-clic pentru a porni aplicația.

instrument control:
GNU Octave poate comunica cu Arduino prin intermediul interfeței seriale, dar nu o poate face în starea inițială. la fel ca în cazul MatLab, e necesară o bibliotecă externă cu instrucțiuni care să-i permită citirea datelor seriale. cea pe care am utilizat-o în curs se numește instrument control și e disponibilă aici.

biblioteca se instalează ușor, descărcând fișierul și mutându-l în directorul src (de la source) al GNU Octave. în cazul în care directorul nu există, îl poți crea pur și simplu. urmează să pornești GNU Octave și cu ajutorul browserului de fișiere din stânga ferestrei principale, găsești și intrii în directorul src. ultimul pas constă în introducerea următoarei comenzi în consola GNU Octave:

>> pkg install instrument-control-0.3.0.tar.gz

instalarea instrument control durează destul de mult așa că poți să iei o pauză de câteva minute.

read_arduino.m:
pentru citirea interfeței seriale a Arduino am scris o mică bucățică de cod în formatul GNU Octave, read_arduino.m care îți pune la dispoziție o funcție cu ajutorul căreia poți citi datele într-o matrice. presupunem că îmi doresc să citesc tensiunea pe fiecare dintre cele 6 intrări analogice și să îi urmăresc evoluția în timp, la intervale de o secundă.

codul pentru Arduino este următorul:

void setup () {
  Serial.begin (9600); // <-- read_arduino.m suportă doar viteza de 9600 bauds
}

void loop () {
  byte port = 0; // <-- am nevoie de o variabila care specifica numarul portului analog
  while (port < 6) { // <-- cel mult voi citi date de la portul 5
    Serial.print (analogRead (port)); // <-- citesc si trimit valoarea citita catre portul serial
    Serial.print ("\t"); // <-- pentru a separa valorile, folosesc caracterul TAB (\t)
    port = port + 1; // <-- trec la portul urmator
  }
  Serial.print ("\n"); // <-- pentru a separa sirurile de date, folosesc caracterul NEW LINE (\n)
  delay (1000); // <-- aștept o secundă pentru a relua ciclul
}

care ar trebui să producă la fiecare secundă câte o linie cu valori cuprinse între 0 și 1023, corespunzătoare diferenței de potențial între pinul GND și pinii de la A0 la A5.

pentru a citi 10 rânduri conținând cele 6 valori, vei folosi:

>> A = read_arduino ('COM5', 10, 6)
A =

   339   339   326   319   312   324
   328   330   319   313   309   319
   327   329   319   314   310   319
   324   327   317   312   308   318
   323   326   316   311   307   317
   324   327   317   312   309   318
   322   324   314   310   306   315
   323   326   317   312   307   317
   324   327   317   312   308   318
   323   326   316   311   307   317

note de final:
COM5 este portul serial pe care se conectează plăcuța mea Arduino. în cazul tău, acesta va fi cel mai probabil diferit. verifică în Device Manager care este portul corect. read_arduino.m funcționează și pe Linux și pe MAC, însă în locul COM5 vei folosi denumirea portului corespunzătoare platformei. portul serial trebuie să fie liber, drept pentru care amintește-ți ca înainte să rulezi read_arduino să închizi monitorul serial al Arduino. ca în cazul MathWorks Matlab, fișierul read_arduino.m în directorul curent pentru ca funcția să devină accesibilă.

bogdan » arduino: senzor de lumină cu led

11:20 am on Mar 25, 2018 | #more | tags:

în urma rugăminții profesorului Stamatin, de câțiva ani țin un curs la Facultatea de Fizică din Măgurele. dacă inițial numele «modelare și simulare» ascundea ecuații matematice și metode numerice, recent am înlocuit diferențialele cu Arduino, în ovațiile celor câtorva studenți care frecventează cursul. dotările limitate m-au făcut să devin creativ cu materialele de curs, iar rezultatul mi s-a părut suficient de interesant pentru a-l reproduce aici. așa că:

senzorii de lumină domină lumea simțurilor electronice. cu mici modificări, aceștia pot răspunde unei multitudini de întrebări, de la banalul ”e lumină afară?”, la ”ce culoare are un obiect?” sau ”la ce distanță am un obstacol în față?”. la prima vedere funcționează complex: un fragment de siliciu reacționează la lumina incidentă, modificând o mărime electrică. Einstein a luat premiul Nobel pentru explicarea principiului în 1921, deci trebuie să fie complicat. cu toate acestea, tehnologia s-a dezvoltat pe parcursul secolului care a trecut suficient de mult ca să putem reface experimentul în bucătărie.

materiale necesare:

  • un Ardunio, de orice fel, în funcție de cât de familiarizat ești cu el; la curs l-am folosit pe ăsta (31 de lei);
  • un led obișnuit, orice culoare și orice mărime, dar să fie un led simplu; știi că e un led simplu (30 de bani), dacă e foarte ieftin;
  • două fire pentru a lega ledul la Arduino; mie îmi plac astea (3 lei), dar pot fi de orice fel;

puțină teorie:
un led este un dispozitiv semiconductor, de obicei din siliciu, al cărui element activ este vizibil. elementul activ poartă numele de joncțiune, adică locul de întâlnire pentru două materiale cu proprietăți diferite. ce e important de reținut pentru construcția de față este că cele două materiale formează un sandviș cu un mic spațiu între ele, asemeni unui condensator de la fizică. în funcționarea normală, trecerea curentului electric prin acel mic spațiu produce lumină. cu siguranță ai observat că polaritatea e importantă, deoarece lumina și circulația curentului se produc doar într-un singur sens.

ce se întâmplă în schimb când polaritatea e inversată? lipsa curentului electric duce la acumularea de sarcini electrice pe fețele sandvișului, încărcând condensatorul. cum naturii îi plac simetriile, orice rază de lumină incidentă generează perechi de sarcini, care se vor deplasa în direcții opuse, datorită atracției electrostatice. ajunse pe suprafețele sandvișului, acestea vor scădea sarcina acumulată pe condensator, scăzând proporțional și tensiunea electrică.

în mod normal, procesele se întâmplă extrem de repede și sunt extrem de mici ca intensitate. dar aici intervine genialitatea oamenilor care au proiectat Arduino: acesta e suficient de rapid și suficient de sensibil pentru a face față experimentului.

ce se va întâmpla:
Arduino va încărca ledul, alimentându-l invers, după care va număra cât îi ia condesatorului format în jurul joncțiunii ledului pentru a se descărca. pentru a repeta experimentul de la curs, vei conecta ledul cu plusul (piciorușul mai lung, anodul) la GND (ground, 0V) și minusul (piciorușul mai scurt, catodul) la unul dintre terminalele Arduino, cu excepția pinilor 0,1 – care sunt responsabili pentru comunicarea serială și pinul 13, care are deja un led conectat intern și care te va încurca. eu am ales pinul 2.

în pregătirea Arduino, am definit un loc în memorie pentru stocarea informațiilor primite de la led. tipul de date folosit va fi întreg (int), numărând câte perioade de timp condensatorul a fost încărcat. pentru a fi accesibilă de oriunde, definiția se va afla în afara și înaintea celor două funcții speciale Arduino, setup și loop.

int value; // <- asa definesc un loc in memorie, int este tipul, iar value este numele
// pentru ca am definit-o în afara setup și loop, voi putea să o accesez de oriunde, prin nume
void setup() {}
void loop() {}

pentru citirea informațiilor, am folosit interfața serială a Arduino, care va fi inițializată prin:

int value;
void setup() {
  Serial.begin(9600); // <- aici initializez conexiunea seriala cu viteza de 9600 caractere / s
}
void loop() {}

periodic, am încărcat ledul pentru un interval de timp. experimental am ales 1ms.

int value;
void setup() {
  Serial.begin(9600);
}
void loop() {
  pinMode(2, OUTPUT); // <- aici definesc pinul 2 ca fiind pin de iesire
  digitalWrite(2, HIGH); // <- setez tensiunea pe pinul 2 la tensiunea de alimentare a Arduino
  delay(1); // <- astept 1ms, sa incarc condensatorul ledului
}

am măsurat în cât timp tensiunea la bornele ledului scade sub un anumit prag. aici aș fi putut să folosesc convertorul analog-digital din Arduino, dar în unele situații este prea lent. așa că am folosit proprietatea unui pin digital configurat ca intrare de a-și schimba starea în jurul jumătății tensiunii de alimentare a Arduino. astfel, dacă tensiunea la intrare scade sub 2.5V față de GND (pentru un Arduino alimentat la 5V), valoarea citită intern va fi LOW, în timp ce dacă tensiunea crește peste 2.5V, valoarea citită va fi HIGH.

pentru a determina în cât timp tensiunea la bornele ledului scade, am verificat la intervale scurte de timp dacă a scăzut. dacă a scăzut, trimit prin conexiunea serială valoarea înregistrată, altfel, mai aștept puțin timp și verific din nou. experimentând, am folosit ca timp de așteptare 40uS.

int value;
void setup() {
  Serial.begin(9600);
}
void loop() {
  pinMode(2, OUTPUT);
  digitalWrite(2, HIGH);
  delay(1);
  pinMode(2, INPUT);
  value = 0; // <- aici resetez ce stochez în memorie
  while(digitalRead(2) == HIGH) { // <- citesc pinul 2 si verific daca tensiunea depasește pragul
    delayMicroseconds(40); // <- daca e peste prag, lumina e prea slabă și aștept 40uS
    value = value + 1; // <- cresc cu o unitate valoarea stocată
  }
  // <- aici tensiunea la bornele ledului a scazut sub prag
  Serial.println(value); // <- așa că trimit valoarea prin conexiunea serială
}

în funcție de led și de condițiile de iluminare, valorile primite de calculator sunt uneori negative. acest lucru se întâmplă deoarece numărul de repetări depășește valoarea maximă care poate fi stocată într-un segment de memorie de tip întreg. pentru a preveni această situație, am introdus o limitare la 255 a numărului de cicluri pentru care verificarea are loc:

int value;
void setup() {
  Serial.begin(9600);
}
void loop() {
  pinMode(2, OUTPUT);
  digitalWrite(2, HIGH);
  delay(1);
  pinMode(2, INPUT);
  value = 0;
  while((digitalRead(2) == HIGH) && (value < 255)) { // <- verific în plus dacă valoarea stocată e sub 255
    delayMicroseconds(40);
    value = value + 1;
  }
  Serial.println(value);
}

note de final:
ledul este sensibil la același tip de lumină pe care o emite. folosind acest principiu, poți foarte ușor să-l transformi într-un senzor de culoare. de asemenea, un led are de obicei o lentilă care dirijează razele de lumină, limitându-i astfel câmpul vizual, la fel ca în cazul emisiei. folosind această informație, poți adapta foarte ușor un led pentru a măsura distanța.

bogdan » php string output speed test

11:54 pm on Feb 14, 2017 | #more | tags:

during the development of a new resource-sensitive project, i wondered what is the best PHP string output method and thus compared the following:

<?php
/*  A: */	printf ('some value %d != %d' . "\n", $c, $c+1); 
/*  B: */	vprintf ('some value %d != %d' . "\n", [ $c, $c+1 ]); 
/*  C: */	vprintf ('some value %d != %d%s', [ $c, $c+1, "\n" ]); 
/*  D: */	echo sprintf ('some value %d != %d' . "\n", $c, $c+1); 
/*  E: */	echo vsprintf ('some value %d != %d' . "\n", [ $c, $c+1 ]); 
/*  F: */	echo vsprintf ('some value %d != %d%s', [ $c, $c+1, "\n" ]);
/*  G: */	echo 'some value ' . $c . ' != ' . ($c+1) . "\n";
/*  H: */	echo "some value $c != " . ($c+1) . "\n";
/*  I: */	?>some value <?php echo $c; ?> != <?php echo $c+1; ?>
<?php ?>

each statement was run inside a for loop, for one million cycles, for a total number of five times. the results were as follows:

A B C D E F G H I
0.4260 0.5836 0.6412 0.4333 0.5896 0.6781 0.4449 0.4182 0.0361
0.4220 0.6012 0.6631 0.4302 0.6210 0.6805 0.4385 0.4100 0.0361
0.4205 0.6083 0.6674 0.4306 0.6245 0.6935 0.4379 0.4168 0.0378
0.4364 0.6098 0.6697 0.4456 0.6752 0.7455 0.4530 0.4134 0.0361
0.4299 0.5964 0.6594 0.4384 0.5793 0.6538 0.4485 0.4185 0.0365

on average, the best I is 11.37 times faster than the next, followed by H, A, D, G, B, E, C and F, which is worst, 18.90 times slower than I.

bogdan » întotdeauna în al 12-lea ceas!

11:46 am on Nov 29, 2016 | #more | tags:

Cu toate astea, decât să-ţi dai ochii scârbit peste cap o viaţă-ntreagă, în aşteptarea unui salvator, mai bine îţi arunci privirea-n oglindă şi poate descoperi ce stă în puterile tale.

Mă enervează articolele astea: iau o idee bună, o minimizează și o transformă în demagogie electorală. Puterea de a face schimbări stă într-adevăr în fiecare dintre noi, doar că e insignifiantă în urma aia lăsată de ștampilă pe buletinul de vot. E pur și simplu o delegare a răspunderii către un grup de oameni care inevitabil vor fi corupți de sistem. Pentru că sistemul așa a fost gândit și nu a dat greș niciodată în 27 de ani de când a fost reformat.

  • Vrem transparență totală? Câți dintre noi au trimis o cerere prin legea 544/2001?
  • Vrem industrie modernă? Câți dintre noi, care au afaceri, și-au rupt de la gură alegând o mașină ieftină sau transportul în comun în locul unei mașini scumpe pentru a investi diferența în echipamente mai performante?
  • Vrem agricultura micilor fermieri? Câți dintre noi, care au terenuri mici, le muncesc sau le dau în arendă? Câți ne-am gândit să ne asociem și să ne dezvoltăm împreună cu vecinii noștri?
  • Vrem învățământ performant? Câți dintre noi s-au implicat activ și realist în adaptarea programei școlare și a modului de predare în școlile în care învață copiii noștri? Câți dintre noi, ca profesioniști, și-au rupt din timp să încerce să transmită informația acumulată?
  • Vrem cultura vie? Câți dintre noi am mers în weekend la un muzeu sau la o piesă de teatru, în loc să mergem la Mall? Câți dintre noi au încercat să înțeleagă ce-au văzut?
  • Vrem sănătate publică? Câți dintre noi ne facem un control periodic? Câți dintre noi își vaccinează copii sau îi duc la dentist? Câți dintre noi am făcut plângeri la Colegiul Medicilor?
  • Vrem transport rapid? Câți dintre noi mergem cu mașina cu toate locurile ocupate? Câți dintre noi respectă regulile de circulație?
  • Vrem să salvăm mediul? Câți dintre noi nu aruncă gunoaie pe jos? Câți dintre noi economisim apa sau energia electrică? Câți dintre noi nu merg cu mașina, 500 de metri, pentru mici cumpărături?
  • Vrem ca România să arate bine în lume? Câți dintre noi se poartă civilizat și cu bun simț când ieșim din țară?
  • Vrem mai puțină corupție? Câți dintre noi nu au dat sau nu au luat șpagă?
  • Vrem mai puțin clientelism politic? Câți dintre noi, care lucrează în companiile abonate la contracte publice, s-au plâns?

Schimbarea nu e inclusă în tușul unei ștampile. Salvarea nu vine de la un acronim. Bunăstarea nu vine dintr-un set de legi emise, redactate și implementate de incompetenți populari. Soluția va fi întotdeauna la îndemâna fiecăruia dintre noi, în acțiunile noastre zilnice.

bogdan » inocență

10:13 am on Nov 27, 2016 | #more | tags:

Vreau să fiu un om bun.
Care e părerea ta?
Sunt?
Da. Ești. Mie așa mi se pare. Eu nu stau de vorbă cu oameni răi.

bogdan » what is more beautiful

09:12 am on Nov 25, 2016 | #more | tags:

What is more beautiful, my love? Love lost or love found? Don’t laugh at me, my love. I know it, I’m awkward and naive, when it comes to love, and I ask questions straight out of a pop song. This doubt overwhelms me and undermines me, my love. To find or to lose? All around me people don’t stop yearning. Did they lose or did they find? I can’t say. An orphan has no way of knowing. An orphan lacks a first love. The love for his mama and papa. That’s the source of his awkwardness, his naivete. You said to me, on that deserted beach in California: “you can touch my legs.” But I didn’t do it. There, my love, is love lost. That’s why I’ve never stopped wondering, since that day: where have you been? And where you are now? And you, shining gleam of my misspent youth, did you lose or did you find? I don’t know. And I will never know. I can’t even remember your name, my love. And I don’t have the answer. But this is how I like to imagine it, the answer. In the end, my love, we have no choice. We have to find.

bogdan » nu sunt antreprenor

12:50 am on Jan 11, 2016 | #more | tags:

nu îndrăznesc să spun despre mine că sunt antreprenor. nu mă ridic la înălțimea standardelor stabilite în literatura de specialitate. nici pe departe. nu m-am dezvoltat personal și nici nu am învățat să fac business.

sunt aproape doi ani de când îmi asum riscuri pentru a-mi construi propriul drum și nu pot să spun exact că mi-a reușit și nici că am eșuat. pentru că în viață, am învățat, lucrurile nu sunt absolute.

am pornit un start-up pentru că-mi place să visez și aș vrea, la un moment dat, să-mi urmez visul. felul ăsta de activitate îmi va pune la dispoziție resursele de care am nevoie să visez cu ochii deschiși. am încercat și alte metode.

am fost angajat. și deși am avut parte e oameni extraordinari care au fost alături de mine să mă îndrume și de la care am învățat o mulțime de lucruri, nu era locul meu acolo. pentru că un loc de muncă îți ia ceea ce ai tu mai de preț. la mine era creativitatea de care aveam nevoie să visez. mi-au oferit în schimb bani și liniște. n-a fost suficient.

am fost finanțat. și din nou, oamenii cu care am pornit la drum au fost minunați și din nou am învățat de la ei o mulțime de lucruri, dar din nou locul meu nu era acolo. un finanțator te lasă să faci ceea ce-și dorești. dar îți impune limite. pe care la început nu le observi sau crezi că poți să le ignori, până când libertatea ta dispare. imaginează-ți că te obligă cineva să visezi în fiecare seară vacanțe exotice și ziua, un om care nu poate să viseze te auditează pentru a-ți verifica calitatea viselor. mi-a oferit în schimb bani și o parte din vis. n-a fost suficient.

sunt propriul meu angajat. sunt creativ pentru clienți în limita pe care mi-o permit fără să-mi afecteze visul. îmi impun limite la vis pentru a le putea depăși. e greu și muncesc de cel puțin două ori mai mult decât am muncit în oricare dintre situașiile anterioare. n-am avut vacanță de când am început. am oameni care depind de mine și de care trebuie să am grijă pentru că ei au grijă de visul meu. mi-am oferit libertatea de a visa și parțial bani. să-mi împlinesc visul a devenit o problemă de timp.

ce-am învățat în aproape doi ani de indendență? că trebuie să muncești foarte mult și trebuie să fii dedicat visului tău. că la un moment dat trebuie să găsești oameni la fel de dedicați ca și tine și să faci tot posibilul să-i păstrezi. că întotdeauna primul răspuns corect la o întrebare este «nu». că e important să fii sincer față de clienții, furnizorii și angajații cu care îți dorești să lucrezi: nu trebuie să epatezi și nici să aplici tehnici speciale de negociere. că ai nevoie de avocat și contabil. și din nefericire, că Mr. Burns are dreptate:

Family, religion, friendship. These are the three demons you must slay if you wish to succeed in business.
— Charles Montgomery ”Monty” Burns

green-rocket

aceast sait folosește cookie-uri pentru a îmbunătăți experiența ta, ca vizitator. în același scop, acest sait utilizează modulul Facebook pentru integrarea cu rețeaua lor socială. poți accesa aici politica mea de confidențialitate.