Utilizarea limbajului de asamblare în Arduino IDE

Ce este ”Inline Assembler”?

Facilitatea de ”Inline Assembler” permite inserarea de cod în limbaj de asamblare în cadrul programelor de C/C++ compilate cu ajutorul GCC (compilatorul utilizat de mediul Arduino IDE). Utilizarea de cod în limbaj de asamblare permite optimizarea unor porțiuni de cod și obținerea unor programe mai mici ca dimensiune (în format binar). Pentru mai multe informații se recomandă consultarea materialului „Inline Assembler Cookbook”.

Inserarea de cod în limbaj de asamblare se face utilizând directiva asm (sau ___asm___) direct în program. De exemplu (instrucțiunea NOP în limbaj de asamblare nu are nici un efect):

asm ( „nop \n”);

Bineînțeles, în cadrul secțiunii de cod în limbaj de asamblare nu vom beneficia de aceleași avantaje și înlesniri ca într-un program obișnuit în limbaj de nivel înalt. Pentru a înțelege mai bine vom reface exemplul clasic din Arduino IDE – programul Blink:

void setup() {

  pinMode(13, OUTPUT);

}

void loop() {

  digitalWrite(13, HIGH);

  delay(1000);

  digitalWrite(13, LOW);  

  delay(1000);     

}

O variantă a acestui program utilizând directiva asm este:

void setup() {

 asm(„sbi  0x04, 0x5 \n”);

}

void loop() {

 asm(„cbi  0x05, 0x5 \n”);  

 delay(1000);                

 asm(„sbi  0x05, 0x5 \n”); 

 delay(1000);           

}

După cum se poate observa instrucțiunile pinMode și digitalWrite, specifice mediului Arduino IDE, au fost înlocuite cu instrucțiuni în limbaj de asamblare: sbi și cbi ce permit setarea sau ștergerea unui bit de la o anumită adresă din memorie. Mai mult decât atâta, nu am mai folosit referința la pinul plăcii Arduino așa cum suntem obișnuiți (pinul 13) ci adrese de memorie la care se află registrele interne de configurare ale pinului (registrul de sens DDRB – adresa 0x04 și registrul de ieșire PORTB – adresa 0x05, în ambele registre am manipulat bitul 5 corespondent pinului PB5 adică pinul 13 al plăcii Arduino). Comparați memoria program ocupată de exemplul original și cel care utilizează directiva asm.

2

Registre interne și echivalarea pinilor între mediul Arduino și arhitectura microcontrolerului ATmega328P

Pentru a ușura lucrul cu pinii I/O mediul Arduino IDE are propria modalitate de identificare a acestora (D0-D13, A0-A5) dar în realitate aceștia sunt organizați în trei porturi a câte 8 pini (PB0-PB7, PC0-PC7, PD0-PD7), echivalența între cele două organizări este reprezentată în diagrama următoare (nu toți pinii sunt prezenți la varianta THT a circuitului ATmega328P):

3

Pentru a putea manipula pinii microcontrolerului (la nivel de limbaj de asamblare) este nevoie să cunoaștem adresele registrelor DDRx (registrul de sens) și PORTx (registrul de ieșire) al portului din care face parte pinul. Pentru mai multe informații despre organizarea internă a registrelor interne este utilă consultarea manualului circuitului ATmega328P.

4 

Exemplu de program: joc de lumini

Presupunem următoarea schemă de interconectare a 8 leduri cu placa de dezvoltare Arduino Uno în mod individual – fiecare led este comandat în mod direct de câte un pin al plăcii de dezvoltare (led 1 – pin 2, led 2 – pin 3…. led 8 – pin 9):

5

Funcționalitatea sistemului va consta în realizarea a două jocuri de lumini. Secțiunea setup a programului va trebui să configureze toți cei opt pini utilizați ca fiind pini de ieșire. Varianta inițială ce utilizează instrucțiunea pinMode este:

void setup() {               

  pinMode(2, OUTPUT);

  pinMode(3, OUTPUT);  

  pinMode(4, OUTPUT); 

  pinMode(5, OUTPUT);   

  pinMode(6, OUTPUT); 

  pinMode(7, OUTPUT); 

  pinMode(8, OUTPUT); 

  pinMode(9, OUTPUT);

  Serial.begin(9600);

}

Prima variantă propusă utilizează instrucțiunea sbi ca și în exemplul precedent (adresa 0x0a este adresa registrului DDRD iar adresa 0x04 adresa registrului DDRB):

asm (

  „sbi 0x0a, 2 \n”

  „sbi 0x0a, 3 \n”

  „sbi 0x0a, 4 \n”

  „sbi 0x0a, 5 \n”

  „sbi 0x0a, 6 \n”

  „sbi 0x0a, 7 \n”

  „sbi 0x04, 0 \n”

  „sbi 0x04, 1 \n”

 );

O variantă mai scurtă este configurarea biților din cele două registre (DDRD și DDRB) simultan utilizând registrul de uz general R26 ca intermediar pentru transmiterea valorii către cele două registre:

asm (

    „ldi r26, 0b11111100 \n”

    „out 0x0a, r26 \n”

    „ldi r26, 0b00000011 \n”

    „out 0x04, r26 \n”

    : : : „r26”

    );

Utilizarea unui registru de uz general trebuie semnalizată compilatorului pentru a nu apărea suprapuneri în utilizarea registrelor – ultima linie din cod, a se vedea și materialul „Arduino Inline Assembly Tutorial #3 (Clobbers)”.

Prima variantă de joc de lumini va aprinde alternativ la un interval de 1 secundă ledurile de rang impar și ledurile de rang par (led 1, led 3, led 5, led 7 – led 2, led 4, led 6, led 8). Secțiunea loop (utilizând cod Arduino) este:

void loop() {

  for (int i=2; i<10; i++) {

      if ((i%2)==0) digitalWrite(i,HIGH);

      else digitalWrite(i,LOW);

   }

   delay(1000); 

   for (int i=2; i<10; i++) {

      if ((i%2)==0) digitalWrite(i,LOW);

      else digitalWrite(i,HIGH);

   }

   delay(1000);

}

Transpunerea în limbaj de asamblare este:

void loop() {

asm (

      „sbi 0x0b,2 \n”

      „cbi 0x0b,3 \n”

      „sbi 0x0b,4 \n”

      „cbi 0x0b,5 \n”     

      „sbi 0x0b,6 \n”

      „cbi 0x0b,7 \n”

      „sbi 0x05,0 \n”

      „cbi 0x05,1 \n”

      „ldi r25, 0x7F \n”

      „wait1: ldi r26, 0xFF \n”

      „wait2: ldi r27, 0xFF \n”

      „wait3: dec r27 \n”

      „nop \n”

      „brne wait3 \n”

      „dec r26 \n”

      „brne wait2 \n”

      „dec r25 \n”

      „brne wait1 \n”     

      „cbi 0x0b,2 \n”

      „sbi 0x0b,3 \n”

      „cbi 0x0b,4 \n”

      „sbi 0x0b,5 \n”     

      „cbi 0x0b,6 \n”

      „sbi 0x0b,7 \n”

      „cbi 0x05,0 \n”

      „sbi 0x05,1 \n”

      „ldi r25, 0x7F \n”

      „wait1b: ldi r26, 0xFF \n”

      „wait2b: ldi r27, 0xFF \n”

      „wait3b: dec r27 \n”

      „nop \n”

      „brne wait3b \n”

      „dec r26 \n”

      „brne wait2b \n”

      „dec r25 \n”

      „brne wait1b \n”

      : : : „r25” , „r26”, „r27”

  ); 

Cea de a doua variantă de joc de lumini va aprinde unul câte unul (pornind de la led-ul 1 până la led-ul 8) toate led-urile și apoi le va stinge în mod similar (în ordine inversă). Operația de aprindere sau stingere a unui led se va efectua la un interval de 500 milisecunde. Secțiunea loop (utilizând cod Arduino) este:

void loop() {

   for (int i=2;i<10;i++) {

     digitalWrite(i,HIGH);

     delay(500); }

   for (int i=9;i>1;i–) {

     digitalWrite(i,LOW);

     delay(500);

   }

  }

Transpunerea în limbaj de asamblare este:

void loop() {

asm (

      „start: sbi 0x0b,2 \n”

      „rcall wait \n”

      „sbi 0x0b,3 \n”

      „rcall wait \n”

      „sbi 0x0b,4 \n”

      „rcall wait \n”

      „sbi 0x0b,5 \n”

      „rcall wait \n”     

      „sbi 0x0b,6 \n”

      „rcall wait \n”

      „sbi 0x0b,7 \n”

      „rcall wait \n”

      „sbi 0x05,0 \n”

      „rcall wait \n”

      „sbi 0x05,1 \n”

      „rcall wait \n”     

      „cbi 0x0b,2 \n”

      „rcall wait \n”

      „cbi 0x0b,3 \n”

      „rcall wait \n”

      „cbi 0x0b,4 \n”

      „rcall wait \n”

      „cbi 0x0b,5 \n”

      „rcall wait \n”     

      „cbi 0x0b,6 \n”

      „rcall wait \n”

      „cbi 0x0b,7 \n”

      „rcall wait \n”

      „cbi 0x05,0 \n”

      „rcall wait \n”

      „cbi 0x05,1 \n”

      „rcall wait \n”     

      „rjmp start \n”

      „wait: ldi r25, 0x3F \n”

      „wait12: ldi r26, 0xFF \n”

      „wait22: ldi r27, 0xFF \n”

      „wait32: dec r27 \n”

      „nop \n”

      „brne wait32 \n”

      „dec r26 \n”

      „brne wait22 \n”

      „dec r25 \n”

      „brne wait12 \n”

      „ret \n”

      : : : „r25” , „r26”, „r27”

  );

}

Ambele jocuri de lumini utilizează instrucțiunile sbi și cbi pentru manipularea pinilor I/0 (pentru o listă completă a instrucțiunilor în limbaj de asamblare se recomandă parcurgerea materialului „AVR Instruction Set Manual”). Funcția de temporizare delay din mediul Arduino IDE este înlocuită cu o succesiune de bucle ce realizează o întârziere aproximativă (în a doua variantă această succesiune este implementată ca o subrutină).

Toilet Time

Ignorat sau râvnit, tihnit sau zbuciumat, relaxare sau chiar plăcut timp de lectură… putem descrie în multe moduri timpul petrecut la toaletă dar știm de fapt care este acest timp? În cadrul proiectului de față vom prezenta realizarea unui sistem ce permite măsurarea timpului petrecut la toaletă. Chiar dacă poate părea puțin bizară tematica propusă vă asigurăm că mulți pasionați de sisteme electronice programabile s-au gândit cel puțin o dată la un sistem care să accesorizeze toaleta proprie; câteva exemple de astfel de proiecte:

Sistemul propus de noi se bazează pe o placă de dezvoltare Adafruit Feather HUZZAH echipată cu microprocesorul WiFi ESP8266. Conectivitatea WiFi va permite consultarea timpului total petrecut la toaletă prin intermediul oricărui dispozitiv inteligent cu conectivitate de rețea (tabletă, telefon mobil). În plus, specific tuturor plăcilor din familia Feather, placa de dezvoltare se poate alimenta de la un acumulator LiPo de 3.7V permițând crearea simplă a unui sistem independent de alimentarea USB. Detectarea ocupării toaletei se va face cu ajutorul unui buton brick ce se va poziționa sub colacul toaletei. Greutatea colacului nu este suficientă pentru a apăsa butonul, butonul se va apăsa doar dacă toaleta este ocupată. Bineînțeles, se poate înlocui componenta buton brick cu un senzor de apăsare brick dacă se dorește perfecționarea dedectării ocupării toaletei sau dacă se dorește realizarea unui sistem cu mai mulți ”utilizatori”.

Schema de interconectare dintre placa de dezvoltare și brick-ul buton este următoarea:

2

Brick-ul se va alimenta la 3.3V iar ieșirea se va conecta la pinul GPIO12 al plăcii de dezvoltare.

Programul a fost dezvoltat și testat utilizând Arduino IDE 1.8.5 având instalată extensia ESP8266 Community 2.3.0. În cadrul programului trebuie personalizate datele de conectare WiFi (variabilele ssid și password).

#include <ESP8266WiFi.h>

#include <WiFiClient.h>

#include <ESP8266WebServer.h>

#include <EEPROM.h>

const char *ssid = „…”;

const char *password = „…”;

ESP8266WebServer server (80);

#define pinButton 12

Timpul total de utilizarea a toaletei este stocat în variabila globală toilettime ca număr de secunde. Consultarea timpului total se poate face accesând adresa IP a sistemului (oferită prin DHCP de către AP-ul WiFi) dintr-un client web (browser web). Procedura handleRoot() este responsabilă de construirea paginii HTML trimisă către clientul web. Timpul total se va afișa în format HH:MM:SS. Programul poate fi îmbunătățit în această privință pentru a afișa durate mai mari de timp (sub formă de zile, săptămâni, ani).

3

char temp[600];

unsigned long toilettime;

void handleRoot() {

int sec = toilettime % 60;

int min = (toilettime / 60) % 60;

int hr = toilettime / 3600;

snprintf ( temp, 600,”%02d:%02d:%02d”, hr, min, sec);

String page = „<html>\

<head>\

<meta http-equiv=’refresh’ content=’10’/>\

<title>Toillet Time</title>\

<style>\

body { background-color: #cccccc; font-family:

Arial, Helvetica, Sans-Serif; Color: #000088; }\

</style>\

</head>\

<body>\

<h1>Toilet Time</h1>\

<p><b>Your total time is:</b> ” + String(temp) + „

</p>\

</body>\

</html>”;

page.toCharArray(temp,page.length()+1);

server.send ( 200, „text/html”, temp );

}

Variabila ocupat are semnificația de toaletă ocupată. Când variabila ocupat are valoare true sistemul va contoriza timpul scurs și îl va adăuga la variabila toilettime. Conținutul variabilei toilettime se va salva în memoria flash a plăcii de dezvoltare la fiecare trecere din true în false a variabilei ocupat – acest lucru va asigura păstrarea timpului total și după un reset al plăcii de dezvoltare. Pentru operațiile cu memoria flash programul utilizează biblioteca Arduino IDE EEPROM. Placa de dezvoltare nu are memorie EEPROM, versiunea pentru ESP8266 a bibliotecii EEPROM utilizează aceleași funcții și metode ca la plăcile Arduino dar se folosește de memoria program flash. În cadrul secțiunii setup(), la fiecare nouă inițializare a programului, variabila toilettime se inițializează din memoria non-volatilă program.

boolean ocupat;

void setup() {

pinMode(pinButton, INPUT);

EEPROM.begin(512);

toilettime = 0;

toilettime += (long)EEPROM.read(0)<<24;

toilettime += (long)EEPROM.read(1)<<16;

toilettime += (long)EEPROM.read(2)<<8;

toilettime += (long)EEPROM.read(3);

WiFi.begin ( ssid, password );

while ( WiFi.status() != WL_CONNECTED ) {

delay ( 500 );

}

server.on ( „/”, handleRoot );

server.begin();

ocupat = false;

}

Variabilele start și stopp memorează momentele trecerii variabilei ocupat din false în true (așezarea pe toaletă) și din true în false (ridicarea de pe toaletă). În momentul adunării timpului scurs la variabila globală toilettime, conținutul acesteia este salvată și în memoria flash.

unsigned long start, stopp;

void loop() {

if ((!ocupat) && (digitalRead(pinButton)==1)) {

start = millis();

ocupat = true;

}

if (ocupat && (digitalRead(pinButton)==0)) {

stopp = millis();

ocupat = false;

toilettime += (stopp – start)/1000;

byte temp;

temp = (toilettime>>24) & 0xFF;

EEPROM.write(0, temp);

temp = (toilettime>>16) & 0xFF;

EEPROM.write(1, temp);

temp = (toilettime>>8) & 0xFF;

EEPROM.write(2, temp);

temp = toilettime & 0xFF;

EEPROM.write(3, temp);

EEPROM.commit();

}

server.handleClient();

}

În cazul în care vă plictisiți testând sistemul vă recomandăm aplicația mobilă Android Toilet Time – Mini-games for the bathroom.

New Year Countdown

Un element important al oricărui revelion este, bineînțeles, numărătoarea inversă până la trecerea în noul an – cronometrul care ne arată cât timp mai este până la deschiderea sticlei de șampanie și până la pornirea focurilor de artificii. În cadrul proiectului de față ne propunem să implementăm un sistem care să ne arate cu precizie cât timp mai este până la un anume moment de timp, poate să fie trecerea în noul an, o aniversare sau orice alt moment important.

Pentru a putea calcula cu exactitate timpul rămas trebuie să știm în primul rând data și ora la momentul actual. Pentru acest lucru vom utiliza o placă cu conectivitate WiFi ce ne va permite sincronizare de timp NTP și anume placa de dezvoltare Adafruit Feather M0 WiFi. Această placă este similară cu placa Arduino MKR1000, montajul propus poate fi realizat cu oricare dintre ele. Pe lângă conectivitatea WiFi, ambele plăci de dezvoltare permit alimentarea de la un acumulator LiPo de 3.7V permițând astfel realizarea simplă a unui montaj portabil. Pentru afișare vom utiliza un modul cu afișaj pe 7 segmente cu 4 caractere și circuit de comandă TM1637. Schema de interconectare dintre placa de dezvoltare și modulul de afișare este:

2

Modulul de afișare se va alimenta la 3.3V (pinul VCC se va conecta la pinul de 3V al plăcii de dezvoltare iar pinul GND la pinul de GND). Pinul CLK se va conecta la pinul 5 al plăcii de dezvoltare și pinul DIO la pinul 6. Comunicația între placa de dezvoltare și modulul de afișare se face serial.

Programul a fost dezvoltat și testat utilizând Arduino IDE 1.8.5 având instalate bibliotecile TM1637Display, Time 1.5.0, WiFi101 0.14.5 și extensia Adafruit SAMD Boards 1.0.21.

#include <TM1637Display.h>

#define CLK 5

#define DIO 6

TM1637Display display(CLK, DIO);

#include <SPI.h>

#include <WiFi101.h>

#include <WiFiUdp.h>

În cadrul programului trebuie personalizate informațiile de conectare WiFi (ssid[] și pass[]). Constanta timeZone conține decalajul de fus orar specific zonei grografice în care ne aflăm (2 pentru ora de iarnă în România).

int status = WL_IDLE_STATUS;

char ssid[] = „…”;       

char pass[] = „…”;   

unsigned int localPort = 2390;

IPAddress timeServer(129, 6, 15, 28);

const int NTP_PACKET_SIZE = 48;

byte packetBuffer[ NTP_PACKET_SIZE];

WiFiUDP Udp;

#include <TimeLib.h>

const int timeZone = 2;

tmElements_t TheDay;

Structurile SEG_DONE și dy conțin configurația de segmente pentru mesajul done și pentru literele d și Y. Mesajul done va fi afișat după ce numărătoare inversă se va încheia. Litele d și Y vor fi utilizate când se vor afișa anii și zilele rămase pâna la evenimentul programat.

const uint8_t SEG_DONE[] = {

  SEG_B | SEG_C | SEG_D | SEG_E | SEG_G,           // d

  SEG_A | SEG_B | SEG_C | SEG_D | SEG_E | SEG_F,   // O

  SEG_C | SEG_E | SEG_G,                           // n

  SEG_A | SEG_D | SEG_E | SEG_F | SEG_G            // E

  };

const uint8_t dy[] = {

  SEG_B | SEG_C | SEG_D | SEG_E | SEG_G,           // d

  SEG_B | SEG_E | SEG_F | SEG_G ,                  // Y

  };

În cadrul secțiunii setup se realizează conectarea la rețeaua WiFi și programarea evenimentului dorit (în exemplul nostru 1 ianuarie 2018 ora 0:00, anul este înregistrat ca diferență față de anul 1970). Instrucțiunea WiFi.setPins este necesară pentru placa Adafruit Feather M0, se va șterge în cazul plăcii Arduino MKR1000.

void setup() {

  WiFi.setPins(8,7,4,2);

  if (WiFi.status() == WL_NO_SHIELD) {

    while (true);

  }

  while ( status != WL_CONNECTED) {

    status = WiFi.begin(ssid, pass);

    delay(10000);

  }

  Udp.begin(localPort);

  setSyncProvider(getNtpTime);

  setSyncInterval(3600);

  TheDay.Second = 0;

  TheDay.Minute = 0;

  TheDay.Hour = 0;

  TheDay.Day = 1;

  TheDay.Month = 1;

  TheDay.Year = 48;

  }

Secțiunea loop implementează partea de verificare a diferenței de timp între momentul actual și data evenimentului (diferența se calculează în secunde și este stocată în variabila dif) și partea de afișare. Dacă diferența este negativă înseamnă că evenimentul a trecut și se afișează done. Dacă diferența este mai mare de 31556926 secunde (adică un an) pe ecran se va afișa diferența în ani întregi, pe trei caractere urmate de litera Y. Dacă diferența este mai mare de 86400 secunde (adică o zi) pe ecran se va afișa diferența în zile, pe trei caractere urmate de litera d. Dacă diferența este mai mică de o zi dar mai mare de o oră (3600 secunde) pe ecran se va afiș diferența în ore:min. În final, când avem un interval mai mic de o oră, pe ecran se va afișa diferența în format min:sec.

void loop() {

  time_t dif;

  dif = makeTime(TheDay) – now();

  display.setBrightness(0x0f);

  if (dif < 0) display.setSegments(SEG_DONE);

  else if (dif>31556926L) {

    display.showNumberDec(((1970+TheDay.Year) –

(year()+1)),true,3,0);

display.setSegments(dy+1, 1, 3); }

  else if (dif>86400L) {

display.showNumberDec((dif/86400L),true,3,0); display.setSegments(dy, 1, 3); }

  else if (dif>3600) {

display.showNumberDecEx((dif/3600L),0xFF,true,2,0); display.showNumberDec(((dif%3600L)/60),true,2,2); }

  else {

display.showNumberDecEx((dif/60),0xFF,true,2,0); display.showNumberDec((dif%60L),true,2,2); }

  delay(1000);

}

Funcții getNtpTime și sendNTPpacket sunt utilizate de biblioteca Time pentru sincronizarea de timp.

time_t getNtpTime() {

   sendNTPpacket(timeServer);

  delay(1000);

  if ( Udp.parsePacket() ) {

    Udp.read(packetBuffer, NTP_PACKET_SIZE);

    unsigned long highWord = word(packetBuffer[40],

packetBuffer[41]);

    unsigned long lowWord = word(packetBuffer[42],

packetBuffer[43]);

    unsigned long secsSince1900 = highWord << 16 | lowWord;

    const unsigned long seventyYears = 2208988800UL;

    unsigned long epoch = secsSince1900 – seventyYears;

    return (time_t) (secsSince1900 – 2208988800UL +

timeZone * SECS_PER_HOUR);

  }

  else return 0;

}

unsigned long sendNTPpacket(IPAddress& address) {

  memset(packetBuffer, 0, NTP_PACKET_SIZE);

  packetBuffer[0] = 0b11100011;  

  packetBuffer[1] = 0;    

  packetBuffer[2] = 6;    

  packetBuffer[3] = 0xEC;

  packetBuffer[12]  = 49;

  packetBuffer[13]  = 0x4E;

  packetBuffer[14]  = 49;

  packetBuffer[15]  = 52;

  Udp.beginPacket(address, 123);

  Udp.write(packetBuffer, NTP_PACKET_SIZE);

  Udp.endPacket();

}

Dacă se dorește implementarea unui afișaj pe 7 segmente utilizând o componentă proprie (un afișaj de mari dimensiuni de exemplu) se poate utiliza un circuit de tip shift register cu o schemă de interconectare de genul:

3

Pentru circuitul 74HC595 se poate avea în vedere utilizarea bibliotecii SparkFun 74HC595 Arduino Library pentru implementarea comenzii către caracterele pe 7 segmente.

Yun Christmas Lights

Luminițele mele de Crăciun rulează Linux! E un lucru cu care putem uimi chiar și cel mai versat invitat. Mai mult decât atât, putem să le comandăm direct de pe telefonul mobil prin WiFi. Acest lucru este posibil și destul de ușor de implementat utilizând o placă de dezvoltare Arduino Yun. Această placă de dezvoltare rulează distribuția Linux OpenWRT și combină conectivitatea de rețea a procesorului Atheros AR9331 cu posibilitatea de comandă în timp real a microcontrolerului ATmega32U4. Pentru lumini vom utiliza 25 de LED-uri RGB WS2801. Aceste LED-uri se comandă simplu prin intermediul a doar două fire (unul de clock și unul de date). Conectarea dintre placa de dezvoltare și LED-uri este următoarea:

2

Pinul D6 al plăcii de dezvoltare se va conecta la firul verde al șirului de LED-uri (firul de clock) iar pinul D5 la firul galben (firul de date). Alimentarea se va face cu ajutorul unei surse de tensiune de 5V minimum 2A, alimentarea va fi comună pentru placa de dezvoltare și pentru LED-uri. Nu alimentați montajul prin portul USB al plăcii de dezvoltare deoarece un port USB nu poate oferi un curent mai mare de 500mA. Pentru mai multe informații legate de utilizarea șirului de LED-uri puteți consulta și materialul „12mm LED Pixels”.

O alternativă mai ieftină pentru sistem este utilizarea plăcii Arduino Industrial 101 – placă mai ieftină și de dimensiuni mai mici. Identică din punct de vedere hardware cu placa Arduino Yun, placa Arduino Industrial 101 expune un număr mai mic de pini ai microcontrolerului ATmega32U4 și nu are interfață ethernet – ambele dezavantaje nu afectează cu nimic montajul propus în proiectul de față. Interfațarea cu șirul de LED-uri în cazul plăcii Arduino Industrial 101 este următoarea:

3

Atât programul cât și configurația conexiunii WiFi sunt identice în cazul ambelor plăci (Arduino Yun și Arduino Industrial 101).

Utilizarea plăcilor de dezvoltare presupune configurarea inițială a conexiunii WiFi (completarea informațiilor de acces WiFi). Utilizarea conexiunii ethernet nu presupune nici o configurație inițială (valabil doar pentru placa Arduino Yun) – interfața ethernet este configurată implicit să obțină o adresă în mod dinamic prin DHCP. Pentru configurarea inițială se recomandă parcurgere materialului „Getting Started with the Arduino Yún LininoOS”.

Pentru interfața de comandă mobilă vom utiliza serviciul cloud Blynk. Acest serviciu ne va facilita implementarea aplicației de comandă pe dispozitive mobile Android sau iOS. Necesită înregistrarea unui cont gratuit și instalarea aplicației specifice pe dispozitivul mobil. Se poate vedea și materialul „Cum să realizăm un sistem IoT fără să scriem nici o linie de cod?”.

4

Programul pentru placa de dezvoltare a fost dezvoltat și testat utilizând Arduino IDE 1.8.3 având instalate bibliotecile Adafruit WS2801 1.0.0, Blynk 0.4.10 și Time 1.5.0.

#include <Adafruit_WS2801.h>

#include <BlynkSimpleYun.h>

#include <TimeLib.h>

#include <WidgetRTC.h>

WidgetRTC rtc;

int dataPin = 5;

int clockPin = 6;

Adafruit_WS2801 strip = Adafruit_WS2801(25, dataPin,clockPin);

În cadrul programului trebuie personalizat codul de autentificare în cadrul serviciului Blynk. Acest cod se obține în momentul în care se creează aplicație mobilă de comandă.

char auth[] = „…”;

Sistemul va avea 4 stări posibile (starea va fi stocată în variabila mode): starea 0 – LED-urile vor fi stinse, starea 1 – toate LED-urile vor fi aprinse și vor avea aceiași culoare dată de aplicația de comandă, starea 2 – stare implicită, toate LED-uri vor fi aprinse dar vor avea culori diferite, starea 3 – un singur LED va fi aprins și se va plimba de la un cap la altul al șirului de LED-uri. În modul automat (variabila automat este implicit true) se va închide automat la ora 23:00 și va reporni la ora 10:00 a doua zi, modul automat va putea fi dezactivat din interfața de comandă mobilă.

byte mode = 2;

byte lastmode = 255;

int lastcolor = -1;

byte r,g,b;

boolean automat = true;

Secțiunea setup va inițializa conexiunea cu serviciul cloud Blynk, comunicația cu LED-urile WS2801 și va configura biblioteca Time să comunice la un interval de o oră (3600 de secunde) cu obiectul RTC (definit în interfața mobilă de comandă) pentru sincronizarea ceasului.

void setup() {

  Blynk.begin(auth);

  setSyncInterval(60*60);

  strip.begin();

  strip.show();

  r = 0; g = 0; b = 0;

}

Procedurile BLYNK_ tratează evenimentele specifice serviciului cloud preluând comenzile din interfața de comandă mobilă.

BLYNK_CONNECTED() {

rtc.begin();

}

BLYNK_WRITE(V7) {

b = param.asInt();

}

BLYNK_WRITE(V6) {

g = param.asInt();

}

BLYNK_WRITE(V5) {

r = param.asInt();

}

BLYNK_WRITE(V2) {

mode = param.asInt();

}

BLYNK_WRITE(V1) {

automat = param.asInt();

}

În cadrul secțiunii loop se implementează comanda efectivă către șirul de LED-uri WS2801 în conformitate cu informațiile primite din interfața mobilă.

void loop() {

Blynk.run();

if (automat) { if ((hour()>22) || (hour()<10)) mode = 0;

else if (mode==0) mode=2; }

if ((lastmode!=mode) || (lastcolor!=Color(r,g,b))) {

switch (mode) {

case 0:

for (int i = 0; i < strip.numPixels(); i++)

strip.setPixelColor(i, 0);

strip.show();

break;

case 1:

for (int i = 0; i < strip.numPixels(); i++)

strip.setPixelColor(i, Color(r,g,b));

strip.show();

break;

case 2:

for (int i = 0; i < strip.numPixels(); i++)

strip.setPixelColor(i, Wheel(((i * 256 /

strip.numPixels()) + Color(r,g,b)) & 255));

strip.show();

break;

case 3:

for (int i = 0; i < strip.numPixels(); i++) {

if(i>0) strip.setPixelColor(i-1, 0);

strip.setPixelColor(i, Color(r,g,b));

strip.show();

Blynk.run();

delay(200);

}

strip.setPixelColor(strip.numPixels()-1, 0);

strip.show();

break;

}

lastmode = mode;

lastcolor = Color(r,g,b);

}

}

Funcțiile Wheel și Color sunt utilizate pentru a calcula comanda de culoare către șirul de LED-uri WS2801.

uint32_t Wheel(byte WheelPos) {

if (WheelPos < 85) {

return Color(WheelPos * 3, 255 – WheelPos * 3, 0);

} else if (WheelPos < 170) {

WheelPos -= 85;

return Color(255 – WheelPos * 3, 0, WheelPos * 3);

} else {

WheelPos -= 170;

return Color(0, WheelPos * 3, 255 – WheelPos * 3);

}

}

uint32_t Color(byte r, byte g, byte b)

{

uint32_t c;

c = r;

c <<= 8;

c |= g;

c <<= 8;

c |= b;

return c;

}

Aplicația mobilă va conține 4 controale (cost total 900 de credite din cele 2000 oferite gratuit):

5

  • Un control Slider pentru stabilirea modului de funcționare (cost 200 de credite);
  • Un control ZeRGBa pentru stabilirea culorii LED-urilor în modurile de funcționare 1 și 3 (cost 400 de credite);
  • Un control Button pentru activarea / dezactivare funcționării automate a sistemului (cost 200 de credite);
  • Un control Real Time Clock (RTC) pentru sincronizarea timpului (cost 100 de credite).

Controlul Slider va fi conectat la pinul virtual V2 și va lua valori între 0 și 3 (cele patru moduri de funcționare ale sistemului).

6

Controlul zeRGBa va fi conectat la pinii virtuali V5, V6 și V7 ce vor trimite către sistem comanda RGB de culoare.

7

Controlul Button se va configura ca fiind de tip Switch și se va conecta la pinul virtual V1.

8

Pentru alte idei și alternative de realizare se pot trece în revistă și următoarele proiecte:

Sistem antifurt pentru cadourile de Crăciun

Securitatea cadourilor de Crăciun este o problemă care ar trebui să preocupe pe toți părinții responsabili. Hoți, persoane răuvoitoare și mai ales personajul malefic Grinch abia așteaptă să distrugă bucuria copiilor din dimineața zilei de Crăciun.

2

Pentru a împiedica acest lucru este absolut necesar ca toate cadourile să fie dotate cu sisteme de siguranță pentru a preveni furtul de sub bradul de Crăciun. În cadrul acestui proiect vă propunem realizarea unui sistem sofisticat care permite declanșarea unei alarme sonore când un cadou este sustras de sub brad.

Sistemul antifurt este bazat pe o placă de dezvoltare Arduino Uno ce are conectate un mini difuzor brick și un senzor magnetic brick. Mini difuzorul este utilizat pentru a genera sunetul de alarmă iar senzorul magnetic pentru a sesiza dacă cadoul a fost mișcat. Pentru a sesiza mișcarea / mutarea cadoului acesta trebuia să aibă integrat un mic magnet. Mișcarea magnetului este sesizată de senzorul magnetic Hall și semnalizată plăcii de dezvoltare ce declanșează alarma sonoră.

3

Mini difuzorul se va conecta la pinul digital 8 al plăcii de dezvoltare și la pinul de GND. Generarea sunetului de alarmă se va face utilizând funcția tone() din mediul Arduino IDE.

Senzorul magnetic brick se conecta la 5V și GND iar ieșirea (OUT) se va conecta la pinul digital 2 al plăcii de dezvoltare. Ieșirea senzorului va fi ”1” (adică 5V) dacă nu detectează un magnet în apropiere sau ”0” (adică 0V) dacă un magnet se află în imediata apropiere.

4

Schema de interconectare între cele trei componente este următoarea:

5

Programul a fost dezvoltat și testat utilizând mediul Arduino IDE 1.8.3.

#define hallPin 2

#define buzzerPin 8

void setup() {

  pinMode(hallPin,INPUT);

  pinMode(buzzerPin, OUTPUT);

}

void loop() {

  boolean alarma;

  alarma = digitalRead(hallPin);

  if (!alarma) tone(buzzerPin,1000);

  else noTone(buzzerPin);

  delay(5000);

}

După încărcarea programului pe placa de dezvoltare și instalarea sistemului antifurt sub cadou, în cazul în care se îndepărtează cadoul de pe sistemul antifurt se va declanșa alarma sonoră ce va zădărnici tentativa de furt.

În cazul în care dorim să micșorăm dimensiunea sistemului antifurt putem înlocui placa de dezvoltare Arduino Uno cu o placă Arduino Pro Mini. În plus putem alimenta sistemul de la o baterie de 9V și pentru a prelungi durata de funcționare vom configura microcontrolerul să funcționeze în regim de consum redus. Noua schemă de interconectare este:

6

Programul a fost realizat și testa utilizând mediul Arduino IDE 1.8.3 și biblioteca LowPower.

#include „LowPower.h”

#define wakeUpPin 2

#define buzzerPin 8

void setup() {

  pinMode(wakeUpPin,INPUT);

  pinMode(buzzerPin, OUTPUT);

}

void wakeUp(){ }

void loop() {

  attachInterrupt(0, wakeUp, HIGH);

  LowPower.powerDown(SLEEP_FOREVER, ADC_OFF, BOD_OFF);

  detachInterrupt(0);

  while (digitalRead(wakeUpPin)==1) {

    tone(buzzerPin,1000);

    delay(1000);

  }

  noTone(buzzerPin);

}

Spre deosebire de programul anterior, sistemul antifurt va funcționa în mod de consum redus. Declanșarea senzorului magnetic (mișcarea cadoului de pe sistemul antifurt) va declanșa o întrerupere ce va trezi microcontrolerul și va porni alarma sonoră. Dacă cadoul este pus la loc alarma va înceta.

Cum să realizăm un sistem IoT LoRaWAN

În cadrul proiectului ”LoRa meets Robofun IoT” am văzut cum putem realiza un sistem IoT utilizând comunicația radio LoRa. Utilizând module radio LoRa putem transmite date la mare distanță dar pentru implementarea unui sistem IoT este necesară implementarea atât a modulului de achiziție (sau acționare) cât și a sistemului de tip gateway ce face legătura cu rețeaua Internet și cu serviciile cloud IoT. Specificațiile LoRaWAN permit implementare unor rețele radio LoRa standardizate astfel încât sistemele gateway să permită conectarea dispozitivelor IoT după un set de reguli larg acceptate. Realizarea unui sistem IoT LoRaWAN presupune realizare unui sistem de achiziție / acționare care respectă acest set de reguli și se conectează la o infrastructură de gateway-uri deja existentă (nu mai este nevoie să realizăm și să operăm sistemul gateway). Există mai multe rețele de gateway-uri LoRaWAN dar în cadrul acestui proiect vom arăta cum putem realiza un sistem ce folosește rețeaua TTN (The Things Network). Accesul în rețeaua TTN este gratuit deoarece se bazează pe gateway-uri particulare partajate între utilizatorii rețelei. Tot ce trebuie să faceți este să verificați dacă vă aflați în aria de acoperire a unui sistem gateway TTN.

Pentru sistemul IoT vom utiliza o placă de dezvoltare Arduino Uno și un shield Dragino LoRa echipat cu un modul radio LoRa în frecvență de 868MHz. Pentru partea de achiziție vom exemplifica măsurarea temperaturii utilizând un senzor brick conectat la pinul analogic A0 al plăcii de dezvoltare.

3

Pentru implementarea comunicației LoRaWAN vom utiliza biblioteca Arduino-LMIC. Testele au fost realizate utilizând Arduino IDE 1.8.3 și versiunea 1.5.0+arduino-1 a bibliotecii. Programul pleacă de la exemplul ttn-abp al bibliotecii în care vom efectua o serie de mici modificări. În primul rând trebuie să înregistrăm sistemul pe platforma TTN pentru a obține datele de autentificare în rețea:

static const PROGMEM u1_t NWKSKEY[16] = { … };

static const u1_t PROGMEM APPSKEY[16] = { … };

static const u4_t DEVADDR = … ;

Înregistrarea presupune crearea unui cont de utilizator, definirea unei aplicații (Applications) și, în cadrul aplicației, definirea unui dispozitiv (Device). În secțiunea se setări (Settings) a noului dispozitiv trebuie aleasă metoda ABP de activare și debifată opțiunea Frame Counter Checks. Tot în cadrul acestei secțiuni se regăsesc datele de autentificare în rețeua TTN. Pentru mai multe detalii legate de definirea aplicației și dispozitivului în rețeua TTN se poate consulta și materialul „LoRaWAN IoT with Arduino Uno, Dragino v1.3 & TheThingsNetwork”.

4

Tot în secțiunea de inițializare a exemplului se va șterge declarația mesajului mydata (se va defini din nou în program sub o altă formă) și se va modifica intervalul de postare a mesajelor (postarea la 1 minut este destul de agresivă pentru politica de utilizare a rețelei TTN).

const unsigned TX_INTERVAL = 3600;

Shield-ul Dragino LoRa necesită următoarea modificare în structura de definire a pinilor utilizați:

const lmic_pinmap lmic_pins = {

    .nss = 10,

    .rxtx = LMIC_UNUSED_PIN,

    .rst = 9,

    .dio = {2, 6, 7},

};

Ultima modificare adusă exemplului ttn-abp este rescrierea procedurii do_send pentru a trasmite valoare achiziționată de la brick-ul de temperatură în locul mesajului text predefinit. După cum se poate observa se va transmite valoarea returnată de funcția analogRead, prelucrarea numerică pentru a obține valoarea temperaturii se va face în sistemul cloud TTN.

void do_send(osjob_t* j){

    static uint8_t mydata[2];

    int reading = analogRead(A0);

    mydata[0] = highByte(reading);

    mydata[1] = lowByte(reading);

    if (LMIC.opmode & OP_TXRXPEND) {

        Serial.println(F(„OP_TXRXPEND, not sending”));

    } else {

        LMIC_setTxData2(1, mydata, sizeof(mydata), 0);

        Serial.println(F(„Packet queued”));

    }

}

După punerea în funcțiune a sistemului, și dacă vă aflați în aria de acoperire a unui gateway TTN, în consola TTN vor începe să apară valorile transmise de acesta (secțiunea Application Data). După cum se poate observa datele transmise sunt sub forma unui șir de valori în hexazecimal (2 octeți – 16 biți).

5

Pentru a transforma datele primite într-o formă mai ușor de înțeles se va scrie o funcție de decodare (în secțiunea Payload Formats / decoder). Această funcție va avea și rolul de a calcula temperatura echivalentă valorii achiziționate. După implementarea acestei funcții vom putea vedea în secțiunea de Application Data valoarea efectivă a temperaturii.

function Decoder(bytes, port) {

  var decoded = (((((bytes[0]<<8)|bytes[1])*5.0)/1024.0)-0.5)*100;

  return { value:decoded };

}

6

Atenție!!! Platforma TTN nu este o platformă IoT – nu stochează datele preluate de la sistemele LoRaWAN. Datele se pot observa în consolă doar dacă sunt transmise atunci când consola este deschisă. Platforma TTN permite în schimb transmiterea datelor primite prin rețeaua LoRaWAN către alte platforme online inclusiv platforme IoT. În secțiunea Integrations se pot defini diverse mecanisme automate de redirectare a datelor către sisteme precum Cayenne, OpenSensors sau IFTTT. Vom explica în cele ce urmează cum putem transmite datele către serviciul IFTTT care ne va trimite apoi valoarea temperaturii prin email. Bineînțeles, multitudinea de opțiuni oferite de platforma IFTTT permite redirectarea datelor către un serviciu IoT, postarea pe o rețea de socializare sau interacțiunea directă cu alte dispozitive IoT.

Definirea mecanismului automat de trimitere a datelor către serviciului IFTTT presupune adăugarea unui integrator de tipul IFTTT Maker (add integration / secțiunea Integrations). Conexiunea între cele două servicii (TTN și IFTTT) se realizează pe baza Event Name (trebuie să fie identic cu numele declanșatorului IFTTT) și Key (cheie de autentificare oferită de obiectul IFTTT Webhooks).

7

În cadrul platformei IFTTT se va realiza o regulă ce va avea declanșator serviciul Webhooks (Event Name trebuie să fie identic cu cel definit în platforma TTN) și ca efect transmiterea unui email.

8

La fiecare valoare a temperaturii transmisă de sistemul nostru vom primi un email de forma:

9

Pentru mai multe variante de realizare a unui sistem IoT LoRaWAN se pot consulta și următoarele materiale:

Utilizarea bibliotecii LUFA

LUFA (Lightweight USB Framework for AVRs) este o bibliotecă ce implementează stiva USB pentru microcontrolerele Atmel AVR ce dețin un port hardware USB (AVR USB Controller) permițând implementarea de dispozitive USB variate: Android Accessory Host, Audio In Device, MIDI Device, Generic HID Device, Joystick Device, Keyboard Device, Printer Host, Virtual Serial Device etc. Biblioteca LUFA este componenta software care stă la baza funcționării majorității plăcilor Arduino: în cazul Arduino Uno / Arduino Mega biblioteca este utilizată de firmware-ul ce permite microcontrolerelor ATmega8U2/16U2 să realizeze comunicația USB-to-serial și să încarce programul în memoria microcontrolerului central; în cazul Arduino Leonardo / Arduino Micro, plăci echipate cu un microcontroler ATmega32U4, biblioteca este utilizată direct de bootloader-ul plăcii permițând atât comunicația USB-to-serial cât și încărcarea programului.

Plăcile de dezvoltare Arduino nu sunt singurele aplicații embedded ce utilizează biblioteca LUFA, mai jos sunt trecute în revistă câteva dintre multele proiecte bazate pe această bibliotecă:

The Smart Card Detective este un sistem ce permite interceptarea, înregistrarea și modificarea comunicației dintre un smartcard și un cititor de carduri (inclusiv de tip EMV).

1

RFM12B USB light – Stick un dispozitiv de comunicație pentru PC-uri bazat pe microcontrolerul ATmega32U4 și modulul radio FSK RFM12B.

The Finch este un robot educațional gândit pentru a fi folosit în explicarea timpurie a noțiunilor de programare, robotică, sisteme de automatizare.

AD9833-based USB Function Generator este un sistem programabil ce permite generarea unor semnale analogice.

FlySight este un sistem ce permite înregistrarea pe un card de memorie a coordonatelor GPS corelate cu viteza și accelerația pe 3 axe.

2

În cadrul materialului de față vă propunem detalierea a două sisteme bazate pe biblioteca LUFA: (1) transformarea unei plăci Arduino Uno într-un dispozitiv MIDI prin reprogramarea microcontrolerului 16U2 și (2) un cititor de carduri bazat pe microcontrolerul ATmega32U4.

Realizarea unui dispozitiv de tip MIDI

Dispozitivele MIDI sunt dispozitive digitale capabile să genereze sunete sau să transmită comenzi de generare a acelor sunete către alte dispozitive digitale (putem vorbi de un sintetizator sau de o claviatură conectată la un sistem de calcul ce rulează un software de sinteză a notelor). În cadrul proiectului nostru vom transforma o placă de dezvoltare Arduino Uno într-un dispozitiv ce se va conecta la PC prin intermediul unei conexiuni USB și va transmite comenzi specifice MIDI către un software specializat. Pentru a putea modifica comportamentul USB al plăcii (tipul de periferic USB văzut de sistemul de operare) este necesar să modificăm firmware-ul microcrontrolerului 16U2 (prezent pe Uno R3, versiunile anterioare se bazează pe microcontrolerul 8U2). Pentru a reprograma microcontrolerul 16U2 există două posibilități:

3

  • prin intermediul conexiunii USB utilizând un software de programare DFU;
  • utilizând portul ICSP al microcontrolerului 16U2/8U2 (imaginea de mai sus surprinde poziția portului ICSP la diverse variante de plăci Arduino Uno) utilizând un programator ISP (USBtiny – modalitate utilizată în explicațiile următoare – sau USBasp) sau o altă placă Arduino Uno pe post de programator ISP.

Firmware-ul microcontrolerului 16U2/8U2 va fi rescris cu Moco for LUFA, software ce va transforma placa Arduino Uno într-un dispozitiv MIDI – se va download-a arhiva MocoLUFA-110123.tgz și se va utiliza binarul MIDI.hex din directorul HEX. Binarul este compilat pentru microcontrolerul 8U2 dar poate fi utilizat și pentru 16U2 (va funcționa pe orice variantă de placă Arduino Uno). Utilizând utilitarul avrdude se va încărca binarul utilizând comanda (programator USBtinyISP, placă de dezvoltare Arduino Uno R3 echipată cu varianta 16U2):

avrdude -c usbtiny -p m16u2 -U flash:w:MIDI.hex

După încărcare noului firmware, la conectarea plăcii sistemul de operare o va identifica ca dispozitiv MIDI (captură de ecran pe pagina următoare) și va instala driverul implicit (nu este necesar un driver special).

4

Atenție!!! După modificarea firmware-urului microcontrolerului 16U2/8U2 placa Arduino Uno va funcționa doar ca dispozitiv MIDI, nu va mai fi posibilă utilizarea ca o placă de dezvoltare obișnuită – nu va mai fi posibilă reprogramarea acesteia. Pentru revenirea la funcționalitatea inițială se va reîncărca firmware-ul original al plăcii pe microcontrolerul 16U2/8U2 utilizând comanda (firmware-ul original se găsește în directorul de instalare al mediului Arduino IDE în subdirectorul hardware\arduino\avr\firmware\atmegaxxu2):

avrdude -c usbtiny -p m16u2 \

-U flash:w:Arduino-COMBINED-dfu-usbserial-atmega16u2-Uno-Rev3.hex

Din aceste motiv încărcarea programului pe placa Arduino trebuie făcută înainte de modificarea firmware-ului pentru microcontrolerul 16U2/8U2. Programul utilizat va trimite pe serială diverse note conform protocolului MIDI – exemplu preluat din [*]:

void setup() {

Serial.begin(31250);

}

void loop() {

for (int note = 0x1E; note < 0x5A; note ++) {

noteOn(0x90, note, 0x45);

delay(100);

noteOn(0x90, note, 0x00);

delay(100); }

}

void noteOn(int cmd, int pitch, int velocity) {

Serial.write(cmd);

Serial.write(pitch);

Serial.write(velocity);}

Pentru a verifica funcționarea dispozitivului MIDI se poate utiliza orice software specializat, de exemplu: MIDI-OX – utilitar MIDI gratuit. Captura de ecran următoare prezintă modul de configurare a dispozitivului MIDI și captura comenzilor emise de dispozitivul MIDI Arduino utilizând MIDI-OX.

5

 

Plăci de dezvoltare bazate pe microcontrolerul ATmega32U4

Următoarea generație de plăci de dezvoltare Arduino înlocuiește combinația de microcontroler AVR (ATmega328P – Arduino Uno/Ethernet/Mini/Nano sau ATmega2560 – Arduino Mega) și microcontroler AVR cu suport USB (ATmega16U2 sau ATmega8U2), utilizat pentru conversia USB-to-serial necesară programării, cu un singur microcontroler ce include atât performanțele necesare funcționării plăcii de dezvoltare cât și conectivitatea necesară conexiunii USB. Noua generație de plăci de dezvoltare Arduino utilizează microcontrolerul ATmega32U4 pentru realizarea ambelor sarcini, un exemplu de astfel de placă de dezvoltare este Arduino Leonardo – placă de dezvoltare cu formă și performanțe similare cu placa Arduino Uno dar care este echipată cu un singur microcontroler ATmega32U4 ce rulează atât bootloader-ul de încărcare USB cât și programul scris de utilizator. La fel ca și în cazul microcontrolerului ATmega16U2/8U2 (ce echipează Arduino Uno) firmware-ul ce rulează pe microcontrolerul ATmega32U4 se bazează pe biblioteca LUFA. Din acest motiv placa de dezvoltare Arduino Leonardo permite utilizatorului, spre deosebire de placa Arduino Uno, să scrie programe utilizând mediul Arduino IDE care să transforme placa de dezvoltare într-un dispozitiv de tip tastatură sau mouse fără a suprascrie firmware-ul original al plăcii (facilitatea de a se comporta ca un dispozitiv HID USB este deja inclusă în firmware).

Placa Arduino Leonardo nu este singura placă din familia Arduino care este echipată cu microcontrolerul ATmega32U4, alte exemple de astfel de plăci sunt: Arduino Micro, Arduino Yun, Arduino Robot, Arduino Esplora. O placă dezvoltare ce trebuie de asemenea menționată este Teensy 2.0 deoarece este una dintre cele mai mici plăci de dezvoltare bazate pe ATmega32U4 fiind alegerea cea mai bună pentru proiectele ce au constrângeri legate de dimensiune și din care placa de dezvoltare nu se mai recuperează. Un alt avantaj major al acestei plăci de dezvoltare este dat de componentele software suplimentare față de o placă de dezvoltare Arduino Leonardo dar compatibile cu mediul Arduino IDE. Pe baza acestei plăci vom prezenta realizarea dispozitivul următor.

7

 

Realizarea unui dispozitiv de tip Cititor de Carduri

Dispozitivul prezentat este bazat pe exemplul de la adresa. Plecând de la exemplul Mass Storage conținut în biblioteca LUFA, sistemul implementează un dispozitiv USB de citire a unui card de memorie SD (un card reader) combinând exemplul cu biblioteca de lucru cu un card de memorie. Schema de interconectare a componentelor este prezentată în imaginea alăturată. Pentru compilarea / recompilarea proiectului este necesară instalarea mediului WinAVR și se realizează prin comanda make all în consolă de comandă în directorul LowLevelMassStorage+SD al proiectului. Fișierul MassStorage.hex rezultat în urma compilării proiectului se încarcă în memoria microcontrolerului ATmega32U4 utilizând un programator ICSP (inclusiv o placă Arduino Uno pe post de programator ISP). După încărcarea noului firmware, placa de dezvoltare va fi văzută de sistemul de operare ca un dispozitiv de stocare și se vor putea efectua operațiile de bază cu cardul de memorie (citire, scriere, formatare).

8

Un proiect similar (o placă Teensy 2.0 programată să se comporte ca un dispozitiv de stocare) se găsește la adresa. Acest proiect utilizează exact același program ca și exemplul precedent dar explică în mod detaliat modul de conectare a unui adaptor SD (Teensy SD adaptor) și modul în care tot ansamblul de componente poate fi integrat într-o carcasă USB astfel încât să rezulte o formă finală elegantă (imagine alăturată). O idee interesantă lansată de acest proiect este posibilitatea de a realiza dispozitive mixte (HID+MassStorage) ce pot fi utilizate în lansarea unor atacuri informatice (un mouse sau o tastatură ce conține un virus în memoria internă – un troian hardware).

9