Utilizarea bibliotecii V-USB

V-USB este o componentă software ce implementează comunicația USB (low-speed USB / USB 1.1) pentru orice microcontroler din familia Atmel AVR – cu alte cuvinte permite comunicația USB pentru microcontrolerele din această familie chiar dacă nu au un port USB hardware (similar cu biblioteca SoftwareSerial ce permite utilizarea oricăror doi pini ai plăcii Arduino pentru comunicația UART). Prin intermediul acestei biblioteci putem utiliza orice microcontroler Atmel AVR pentru o conexiune directă USB fără a fi nevoie de un circuit suplimentar de conversie UART – USB (FTDI sau microcontroler cu port hardware USB – 8U2 sau 16U2). Singurele restricții sunt ca circuitul microcontroler să aibă cel puțin 2kB memorie flash, 128 bytes memorie RAM și să funcționeze la cel puțin 12MHz – condiții îndeplinite fără probleme de microcontrolerul ATmega328P ce echipează placa de dezvoltare Arduino Uno. O aplicație imediată a acestei biblioteci o reprezintă plăcile de dezvoltare compatibile Arduino ce nu dețin circuite suplimentare față de microcontroler (nu au circuit de conversie UART – USB și implicit au un cost mai mic), de exemplu placa de dezvoltare Adafruit Pro Trinket. Placa Adafruit Pro Trinket este echipată cu un microcontroler ATmega328P, la fel ca și placa Arduino Uno, are dimensiuni și cost apropiate de o placă de dezvoltare Arduino Pro Mini dar nu necesită programator extern FTDI pentru încărcarea programelor. La fel ca și celelalte plăci de dezvoltare Arduino, placa Pro Trinket funcționează pe baza unui bootloader rezident în memoria flash a microcontrolerului ce permite transferarea programului de pe USB în memoria program internă. Bootloader-ul plăcii Pro Trinket se bazează pe biblioteca V-USB și funcționează în mod similar cu bootloader-ele altor plăci Arduino. Singurul dezavantaj al unei astfel de soluții este procedura mai greoaie de upload (de încărcare a programului). Circuitele suplimentare prezente pe plăcile Arduino se ocupau cu transferul programului USB – memorie internă program lansând în execuție bootloader-ul intern printr-o operație de reset a microcontrolerului. Din cauza absenței circuitului care să declanșeze operația de reset această operație trebuie efectuată manual – înainte de operația de upload (încărcare a programului) trebuie apăsat butonul de reset și operația de încărcare trebuie efectuată într-un interval de 10 secunde (atâta timp cât placa se află în Bootloader Mode).

V-USB for Arduino

Aplicabilitatea bibliotecii V-USB nu se rezumă doar la dezvoltarea de bootloadere ce permit încărcarea programului în memoria internă a microcontrolerului. Prin intermediul bibliotecii V-USB putem dezvolta periferice USB bazate pe microcontrolere Atmel AVR de tip HID (Human Interface Device – tastatură, mouse), CDC (Communications Device Class – modemuri, mass storage) sau chiar MIDI (Musical Instrument Digital Interface) fără a fi limitați la clasa de dispozitive USB de tip USB-to-serial (USB Serial Converter) oferite de driverul obișnuit al plăcilor de dezvoltare Arduino. Site-ul V-USB oferă o colecție semnificativă de exemple de proiecte diverse de astfel de dispozitive.

Chiar dacă biblioteca V-USB este scrisă în limbajul C nu poate fi folosită direct în mediul Arduino IDE fiind dezvoltată sub mediul WinAVR. Din fericire există o portare parțială a bibliotecii V-USB sub forma unei biblioteci specifice Arduino IDE și anume V-USB for Arduino. Prin intermediul acestei biblioteci o placă Arduino Uno se poate transforma într-un dispozitiv USB personalizat. Biblioteca este format din trei componente independente: UsbDevice (permite implementarea unui dispozitiv USB generic), UsbKeyboard (permite implementarea unui dispozitiv USB HID de tip tastatură), UsbStream (permite implementarea unui dispozitiv de comunicație / transfer de date).

Biblioteca V-USB for Arduino poate fi utilizată fără nici un fel de problemă sub versiunile Arduino IDE 1.0.x dar necesită o mică corecție sub versiunile mai recente (1.5.x, 1.6.x): declarațiile realizare în memoria program (PROGMEM) trebuie să fie de tip constantă (const) – în toate fișierele .c și .h declarațiile de tip PROGMEM trebuie modificate în PROGMEM const.

Realizarea unui dispozitiv de tip „Carte de vizită”

În cadrul acestei secțiuni vom realiza un periferic de tip HID (mai exact tastatură) ce va completa în mod automat datele de contact ale deținătorului (proiect inspirat de (*)). Sistemul va fi format dintr-o placă de dezvoltare Arduino Uno, un buton (plus o rezistență de 10Kohm) și partea de interfațare electrică cu conexiunea USB (2 diode Zener 3.6V maxim 0.5W, 2 rezistențe 68ohm și 1 rezistență 1.5Kohm). La apăsarea butonului sistemul va trimite pe USB textul de semnătură (carte de vizită) ca și cum ar fi tastat.

Butonul este conectat la pinul digital 10, acesta este ținut la Vcc (+5V) prin intermediul unei rezistențe de 10Kohm. Apăsarea butonului trage pinul digital în ”0” logic (0V). Conexiunea electrică USB presupune ca liniile D+, D- să aibă o rezistență de 68ohm în serie și câte o diodă Zener de 3.6V la masă (pentru adaptarea nivelului logic de 5V la 3.6V). Linia D+ necesită și o rezistență de pull-up de 1.5Kohm comandată de pinul digital 5. Linia D- se conectează la pinul digital 4 și linia D+ la pinul digital 2. Liniile conexiunii USB Vcc și GND se interconectează la pinii 5V și GND ai plăcii Arduino asigurând alimentarea sistemului în modul de funcționare periferic USB de tip HID.  Schema de interconectare este următoarea:

2

Programul ce va rula pe placa de dezvoltare va fi încărcat utilizând Arduino IDE și conexiunea USB a plăcii.

#include „UsbKeyboard.h”

#include <util/delay.h>

#define BUTTON_PIN 10

String mesaj;

void setup() {

  pinMode(BUTTON_PIN, INPUT);

  TIMSK0&=!(1<<TOIE0);

}

void loop() {

  UsbKeyboard.update();

  if (digitalRead(BUTTON_PIN) == 0) {

    mesaj = „ING. POPESCU ION”;

    KeyStrokeAlpNum(mesaj);

    mesaj = „DIRECTOR TEHNIC EMBEDDED DEVICES SRL”;

    KeyStrokeAlpNum(mesaj);

    mesaj = „MOBIL 0789.000.233”;

    KeyStrokeAlpNum(mesaj);

    mesaj = „FIX 021.456.30.03”;

    KeyStrokeAlpNum(mesaj);

    UsbKeyboard.sendKeyStroke(KEY_ENTER);

    _delay_ms(200);   }

}

După încărcarea programului conexiunea USB cu placa de dezvoltare se va deconecta și se va utiliza conexiunea USB prin intermediul mufei de pe breadboard.

În cadrul programului este necesară includerea bibliotecii UsbKeyboard.h. În secțiunea setup se va configura pinul digital 10 ca intrare (pin utilizat pentru buton) și se va dezactiva timerul 0 (utilizat de mediul Arduino IDE pentru funcțiile de temporizare) pentru a nu interfera cu temporizarea utilizată în biblioteca V-USB. Secțiunea loop va conține un apel periodic al procedurii UsbKeyboard.update() – apel necesar pentru a efectua operații obligatorii în cadrul comunicației USB, operații invocate de procedura usbpool() a bibliotecii V-USB; trimiterea de caractere prin intermediul conexiunii USB este condiționată de apăsarea butonului (digitalRead(BUTTON_PIN) == 0) – trimiterea efectivă a codului unei taste se realizează prin intermediul funcțiilor UsbKeyboard.sendKeyStroke(cod_tasta) pentru o singură tastă și KeyStrokeAlpNum(mesaj) pentru un șir de taste.

void KeyStrokeAlpNum(String Sc){

    int sPoint = Sc.length();         

    for (int x = 0; x < sPoint; x++){

      int y = x + 1;

      if (Sc.substring(x, y) == „H”){ 

          UsbKeyboard.sendKeyStroke(KEY_H, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „L”){

          UsbKeyboard.sendKeyStroke(KEY_L, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == ” „){

          UsbKeyboard.sendKeyStroke(KEY_SPACE);

          }

       else if (Sc.substring(x, y) == „1”){

          UsbKeyboard.sendKeyStroke(KEY_1);

          }

       else if (Sc.substring(x, y) == „2”){

          UsbKeyboard.sendKeyStroke(KEY_2);

          }

       else if (Sc.substring(x, y) == „3”){

          UsbKeyboard.sendKeyStroke(KEY_3);

          }

       else if (Sc.substring(x, y) == „4”){

          UsbKeyboard.sendKeyStroke(KEY_4);

          }

       else if (Sc.substring(x, y) == „5”){

          UsbKeyboard.sendKeyStroke(KEY_5);

          }

       else if (Sc.substring(x, y) == „6”){

          UsbKeyboard.sendKeyStroke(KEY_6);

          }

       else if (Sc.substring(x, y) == „7”){

          UsbKeyboard.sendKeyStroke(KEY_7);

          }

       else if (Sc.substring(x, y) == „8”){

          UsbKeyboard.sendKeyStroke(KEY_8);

          }

       else if (Sc.substring(x, y) == „9”){

          UsbKeyboard.sendKeyStroke(KEY_9);

          }

       else if (Sc.substring(x, y) == „0”){

          UsbKeyboard.sendKeyStroke(KEY_0);

          }

       else if (Sc.substring(x, y) == „.”){

          UsbKeyboard.sendKeyStroke(55);

          }

       else if (Sc.substring(x, y) == „-„){

          UsbKeyboard.sendKeyStroke(45);

          }

       else if (Sc.substring(x, y) == „A”){

          UsbKeyboard.sendKeyStroke(KEY_A, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „B”){

          UsbKeyboard.sendKeyStroke(KEY_B, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „C”){

          UsbKeyboard.sendKeyStroke(KEY_C, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „D”){

          UsbKeyboard.sendKeyStroke(KEY_D, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „E”){

          UsbKeyboard.sendKeyStroke(KEY_E, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „F”){

          UsbKeyboard.sendKeyStroke(KEY_F, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „G”){

          UsbKeyboard.sendKeyStroke(KEY_G, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „I”){

          UsbKeyboard.sendKeyStroke(KEY_I, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „J”){

          UsbKeyboard.sendKeyStroke(KEY_J, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „K”){

          UsbKeyboard.sendKeyStroke(KEY_K, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „M”){

          UsbKeyboard.sendKeyStroke(KEY_M, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „N”){

          UsbKeyboard.sendKeyStroke(KEY_N, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „O”){

          UsbKeyboard.sendKeyStroke(KEY_O, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „P”){

          UsbKeyboard.sendKeyStroke(KEY_P, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „Q”){

          UsbKeyboard.sendKeyStroke(KEY_Q, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „R”){

          UsbKeyboard.sendKeyStroke(KEY_R, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „S”){

          UsbKeyboard.sendKeyStroke(KEY_S, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „T”){

          UsbKeyboard.sendKeyStroke(KEY_T, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „U”){

          UsbKeyboard.sendKeyStroke(KEY_U, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „V”){

          UsbKeyboard.sendKeyStroke(KEY_V, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „W”){

          UsbKeyboard.sendKeyStroke(KEY_W, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „X”){

          UsbKeyboard.sendKeyStroke(KEY_X, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „Y”){

          UsbKeyboard.sendKeyStroke(KEY_Y, MOD_SHIFT_LEFT);

          }

       else if (Sc.substring(x, y) == „Z”){

          UsbKeyboard.sendKeyStroke(KEY_Z, MOD_SHIFT_LEFT);

          }

      }

      UsbKeyboard.sendKeyStroke(KEY_ENTER);

}

Realizarea unui dispozitiv de tip mouse

De această dată vom realiza un sistem bazat pe placa de dezvoltare Arduino Pro Mini 5V  (pentru a reduce costurile și dimensiunea sistemului) plecând de la exemplul hid-mouse pus la dispoziție de biblioteca V-USB. Compilarea exemplului necesită instalarea mediului de dezvoltare WinAVR iar încărcarea lui pe placa de dezvoltare necesită un programator ISP (de exemplu Arduino ISP sau Pocket AVR). Schema de interconectare a componentelor (placă de dezvoltare și componente de interfațare USB – similare cu proiectul precedent) este:

3

Pentru încărcarea programului se urmează următorii pași:

  • Se instalează mediul de dezvoltare WinAVR.
  • Se actualizează utilitarul avrdude (avrdude.exe și avrdude.conf) din directorul de instalare al mediului WinAVR (uzual C:\WinAVR-20100110\bin) pentru a putea lucra cu microcontrolerul ATmega328P.
  • Se conectează programatorul ISP pe pinii RST, MOSI (pinul 11), MISO (pinul 12), SCK (pinul 13), GND, 5V ai plăcii de dezvoltare Arduino Pro Mini.
  • Se deschide o fereastră de comandă de tip consolă (click-dreapta cu tasta shift apăsată) pentru directorul hid-mouse (vusb-20121206\examples).
  • În fereastra de comandă se tastează make hex pentru a compila exemplul și a obține fișierul binar hex ce urmează a fi încărcat în memoria microcontrolerului.
  • În fereastra de comandă se tastează comanda

avrdude -c usbtiny -p m328p -U hfuse:0xDE:m -U lfuze:w:0xFF:m

pentru a inițializa biții de fuse ai microcontrolerului; se consideră utilizarea

unui programator USBtinyISP (precum Pocket AVR).

  • În fereastra de comandă se tastează comanda

avrdude -c usbtiny -p m328p –U flash:w:main.hex:i

pentru a încărca programul în memoria internă a microcontrolerului.

  • Se deconectează programatorul ISP și se conectează sistemul prin intermediul conexiunii USB.

Sistemul odată conectat se va comporta ca un dispozitiv de tip mouse (descriptor dispozitiv în imaginea următoare) – cursorul de mouse se va mișca pe ecran pe o traiectorie de tip cerc. Proiectul poate constitui un bun punct de plecare pentru dezvoltarea unor dispozitive de tip mouse – de exemplu se poate încerca dezvoltarea unui sistem care să miște cursorul de mouse cu ajutorul a patru butoane (sus, jos, dreapta, stânga).

4

Automatizarea utilizării dispozitivelor radio ASK

Pornind de la sistemul prezentat în proiectul „Convertor ASK USB” putem dezvolta o funcționalitate foarte simplă dar utilă în cazul utilizării mai multor dispozitive radio în bandă ISM de 433MHz modulație ASK – mai ales dacă vorbim de de dispozitive de la producători diverși, incompatibile nativ între ele. Această nouă funcționalitate a sistemului ne va permite să automatizăm diverse operații simple ca de exemplu: ”la declanșarea unui senzor de prezență sau de deschidere a ușii să se aprindă lumina într-o anumită cameră„ sau ”la declanșarea unui senzor de gaz să pornească alarma sistemului antiefracție„ etc. Sistemul nostru va face legătura între componente radio ce fac parte din dispozitive complet diferite și va putea fi utilizat indiferent indiferent dacă componentele respective mai fac sau nu mai fac parte din dispozitivele inițiale (senzorii și elementele de acționare vor funcționa cu sau fără componenta bază originală).

Vom utiliza aceleași componente ca și în lecția precedentă: placă de dezvoltare Arduino Pro Micro cu un microcontroler ATmega 32U4 la 16MHz / 5V, un transmițător radio și un receptor radio – ambele în bandă de 433MHz modulație ASK. Schema de interconectare este de asemenea la fel (transmițătorul conectat la pinul 10, receptorul la pinul 3):

2

Programul, dezvoltat și testat utilizând Arduino IDE 1.8.1, extensia Arduino AVR Boards 1.6.19 și bibilioteca rc-switch 2.6.2, este puțin diferit:

#include <RCSwitch.h>

RCSwitch mySwitch = RCSwitch();

void setup() {

  while (!Serial);

  Serial.begin(115200);

  mySwitch.enableTransmit(10);

  mySwitch.setRepeatTransmit(3);

  mySwitch.enableReceive(0); }

void loop() {

   if (mySwitch.available()) {

     int value = mySwitch.getReceivedValue();

     if (value != 0) {

      switch (mySwitch.getReceivedValue()) {

        case 6083408:

          Serial.println(„ON command received.”);

          mySwitch.setProtocol(2);

          mySwitch.send(2474994176,32);

          break;

        case 6049984:

          Serial.println(„OFF command received.”);

          mySwitch.setProtocol(2);

          mySwitch.send(2743429632,32);

          break;

        default:

          Serial.print(„Unknown command, received „);

          Serial.print( mySwitch.getReceivedValue() );

          Serial.print(” / „);

          Serial.print( mySwitch.getReceivedBitlength() );

          Serial.print(„bit „);

          Serial.print(„Protocol: „);

          Serial.println( mySwitch.getReceivedProtocol() );

          break;

      }

    }

    mySwitch.resetAvailable();

  }

}

Se poate observa că în cadrul secțiunii loop() se execută o buclă infinită care așteaptă primirea unui cod radio. În funcție de codul radio primit (instrucțiunea switch(mySwitch.getReceivedValue())) se va trimite un alt cod radio ca efect al acțiunii dorite.

3

În cazul exemplului dat s-a utilizat un senzor de deschidere a ușii și o telecomandă radio (ambele făcând parte dintr-un sistem de securitate a locuinței) ca intrări și o priză Conrad RSL ca element de acționare. Pentru mai multe informații legate de diverse dispozitive radio de securitate se poate parcurge și proiectul ”HomeWatch” din cartea ”10(zece) proiecte cu Arduino” și pentru mai multe informații legate de dispozitivele din gama Conrad RSL se pot parcurge și materialele ”Cloud’s Lights” și ”Local Area Power Plugs”.

Codul 6083408 este transmis de senzorul de deschidere a ușii și declanșează deschiderea prizei – cod radio transmis 2474994176. Putem considera că priza alimentează o veioză, o sonerie sau o cameră video de securitate.

4

5

Codul 6049984 este transmis de butonul de armare al telecomenzii radio și va declanșa transmiterea codului radio 2743429632 de închidere a prizei. Orice alt cod radio, necunoscut, va avea ca efect afișarea în consola serială a mesajului Unknown command urmat de codul numeric propriu-zis, acest lucru permite aflarea codurilor ce dorim a fi introduse în program.

6

Funcționarea sistemului nu este limitată la dispozitive radio prefabricate. Putem cu ușurință să dezvoltăm sisteme simple de supraveghere a parametrilor de mediu sau sisteme de acționare pentru diverse sarcini casnice. Un bun exemplu este prezentat în cadrul proiectului ”HomeWatch” din cartea ”10(zece) proiecte cu Arduino” – este vorba de un senzor de bucătărie ce permite avertizarea în caz de inundație sau în cazul unor scurgeri de gaze. Senzorul se bazează pe o placă de dezvoltare Arduino Pro Mini, un modul senzor de gaz metan MQ4 și un transmițător radio 433MHz ASK.

Senzorul de bucătărie poate detecta două evenimente neplăcute ce pot apărea într-o locuință în zona bucătăriei: scurgeri de gaz, senzorul va trimite codul radio 3333310 la apariția acestui eveniment, și scurgeri de apă la nivelul podelei, senzorul va trimite codul radio 3333311 la apariția acestui eveniment. Acest sistem de tip senzor poate fi utilizat cu ușurință împreună cu sistemul de automatizare prezentat putând declanșa o alarmă sonoră sau, de ce nu, un sistem de deschidere a ferestrelor. Schema și codul necesar sunt prezentate în cele ce urmează.

7

Programul a fost testat utilizând Arduino IDE 1.8.1, extensia Arduino AVR Boards 1.6.19 și bibilioteca rc-switch 2.6.2.

#include <RCSwitch.h>

RCSwitch mySwitch = RCSwitch();

volatile unsigned long alarma_apa = 0;

volatile unsigned long alarma_gaz = 0;

const unsigned long interval = 60000;

const unsigned int pin_rf = 10;

const unsigned int pin_gaz = A0;

const unsigned int pin_apa = A1;

const unsigned long cod_gaz = 3333310;

const unsigned long cod_apa = 3333311;

void setup() {

  mySwitch.enableTransmit(pin_rf);

  mySwitch.setRepeatTransmit(10);

  pinMode(pin_apa,INPUT_PULLUP); }

void loop() {

  if (analogRead(pin_gaz)>500)

  {   if (alarma_gaz == 0)

      {

         mySwitch.send(cod_gaz,24);

         delay(3000);

         alarma_gaz = millis();

      }

      else

        if ((millis() – alarma_gaz) > interval)

        {

          mySwitch.send(cod_gaz,24);

          delay(2000);

          alarma_gaz = millis();

        }

   }

   else

     if ((alarma_gaz != 0) && ((millis() – alarma_gaz) > interval) )

     {

       alarma_gaz = 0;

     }

   if (analogRead(pin_apa)<500)

   {

      if (alarma_apa == 0)

      {

         mySwitch.send(cod_apa,24);

         delay(3000);

         alarma_apa = millis();

      }

      else

        if ((millis() – alarma_apa) > interval)

        {

          mySwitch.send(cod_apa,24);

          delay(2000);

          alarma_apa = millis();

        }

   }

   else

     if ((alarma_apa != 0) && ((millis() – alarma_apa) > interval) )

     { alarma_apa = 0; }

}

Proiect Convertor ASK / USB

Multe dintre dispozitivele casnice fără fir funcționează prin comunicație radio în bandă ISM de 433MHz modulație ASK. Cele mai des întâlnite astfel de dispozitive sunt sistemele de securitate fără fir, prizele sau soclurile de bec comandate de la distanță și chiar sistemele meteo cu senzori fără fir. Toate aceste sisteme oferă dispozitive fără fir gata construite, ieftine și foarte ușor de folosit în sisteme de comandă personalizate. Dacă sistemul de comandă este bazat pe o placă de dezvoltare de tipul Arduino nu este nici un fel de problemă deoarece există module radio ASK în bandă de 433MHz cu un cost mic și ușor de folosit. Dacă sistemul de comandă are o complexitate mai mare și necesită utilizarea unui PC sau a unei plăci de tipul Raspberry Pi lucrurile se complică puțin. În cadrul acestui proiect ne propunem să realizăm un sistem ieftin și de mici dimensiuni care să realizeze legătura între comunicația radio în bandă de 433MHz modulație ASK și portul USB prezent în sistemele de calcul de uz general (PC-uri, laptop-uri, plăci de dezvoltare bazate pe microprocesoare). Astfel de dispozitive există deja dar au un preț destul de mare (a se vedea (*)).

Sistemul nostru se va baza pe o placă de dezvoltare Arduino Pro Micro cu un microcontroler ATmega 32U4 la 16MHz / 5V, un transmițător radio și un receptor radio – ambele în bandă de 433MHz modulație ASK. Componentele au fost alese astfel încât să asigure un cost total cât mai scăzut și posibilitatea de a fi integrate într-o carcasă de mici dimensiuni (aproximativ cât o baterie USB externă, imagine alăturată, astfel de carcase se găsesc de cumpărat pe Internet la prețuri de circa 2$).

2

Dispozitivul rezultat va fi văzut de sistemul de calcul în cadrul căruia va fi utilizat ca un dispozitiv serial ce va transmite către sistem codurile radio primite și va transmite radio codurile primite de la sistem – va fi realiza conversia dintre comunicația serială (over USB) și comunicația radio ASK în bandă de 433MHz.

 

Interconectarea componentelor

Interconectarea dintre placa de dezvoltare și cele două module radio este reprezentată în diagrama următoare:

3

Transmițătorul radio va avea linia de date conectată la pinul 10 al plăcii de dezvoltare iar receptorul radio va avea linia de date conectată la pinul 4 al plăcii de dezvoltare (pinul de întrerupere 0). Ambele module se vor alimenta la 5V prin intermediul pinilor VCC / GND ai plăcii de dezvoltare.

Pentru mai multe detalii despre instalarea și operarea plăcii Sparkfun Pro Micro se recomandă parcurgerea materialului ”Pro Micro & Fio V3 Hookup Guide” .

 

Programarea sistemului

Programul a fost dezvoltat și testat utilizând Arduino IDE 1.8.1 cu extensia Arduino AVR Boards 1.6.19 instalată și bibilioteca rc-switch 2.6.2.

 

#include <string.h>

#include <RCSwitch.h>

RCSwitch mySwitch = RCSwitch();

void setup() {

  while (!Serial);

  Serial.begin(115200);

  mySwitch.enableTransmit(10);

  mySwitch.setRepeatTransmit(3);

  mySwitch.enableReceive(0);

}

void loop() {

  char inData[80];

  int index = 0;

  while(Serial.available() > 0)

  {

     char aChar = Serial.read();

     if(aChar == ‘\n’)

     {

        inData[index] = NULL;

        index = -1;

     }

     else

     {

        inData[index] = aChar;

        index++;

        inData[index] = ‘\0’;

     }

  }

  char * number;

  unsigned long number1, number2, number3;

  if (index==-1) {

    number = strtok(inData,”,”);

    number1 = atol(number);

    number = strtok(NULL,”,”);

    if(number!=NULL) { number2=atoi(number); }

    number = strtok(NULL,”,”);

    if(number!=NULL) { number3=atoi(number); mySwitch.setProtocol(number3);}

    mySwitch.send(number1,number2);

  }

  if (mySwitch.available()) {

     int value = mySwitch.getReceivedValue();

        if (value == 0) {

      Serial.print(„Unknown encoding”);

    } else {

      Serial.print( mySwitch.getReceivedValue() );

      Serial.print(„,”);

      Serial.print( mySwitch.getReceivedBitlength() );

      Serial.print(„,”);

      Serial.println( mySwitch.getReceivedProtocol() );

    }

    mySwitch.resetAvailable();

  }

}

Comunicația dintre sistemul de calcul și dispozitivul USB construit va avea mesajele de forma COD,DIMENSIUNE,VERSIUNEPROTOCOL .

De exemplu, în consola serială, apăsarea butoanelor unei telecomenzi radio ce face parte dintr-un sistem de securitate a locuinței va avea următorul efect (se observă codul butonului de dimensiune 24 biți, protocol radio versiunea 1).

4

5

Pentru mai multe informații legate de diverse dispozitive radio de securitate se poate parcurge și proiectul ”HomeWatch” din cartea ”10(zece) proiecte cu Arduino”.

În cazul în care dorim să dăm o comandă radio vom tasta în consola serială cei trei parametrii necesari transmisiei. De exemplu: 2474994176,32,2 (cod de deschidere pe 32 de biți, protocol versiunea 2, specific unei prize radio din gama Conrad RSL).

6

Pentru mai multe informații legate de dispozitivele din gama Conrad RSL se pot parcurge și materialele ”Cloud’s Lights” și ”Local Area Power Plugs”.

Bineînțeles, dispozitivul prezentat nu își găsește utilitatea majoră în comanda sau supravegherea manuală în consola serială. Utilizarea lui trebuie integrată într-o aplicație specifică platformei la care este conectat (o aplicație Windows sau un script de automatizare sub platforma Linux) și sub care pot fi predefinite codurile de comandă pentru diverse dispozitive cunoscute.

O aplicație utilă poate fi utilizarea dispozitivului în conjuncție cu o platfotmă de automatizare a locuinței, de exemplu OpenHAB. În acest scop se pot vedea și materialele ”Realizarea unui sistem de tip Home Automation” și ”Conectarea unor dispozitive antiefracție la platforma OpenHab”.

Proiect Termometru Simfonic

În cadrul proiectului de față vom prezenta o soluție de achiziție (ca exemplificare vom face achiziția temperaturii și umidității mediul ambiant) cu raportare IoT prin GPRS (rețea de telefonie mobilă). Soluția propusă este una simplă și vine în întâmpinarea unei probleme extrem de actuală: supravegherea de la distanță a unor parametrii de mediu în locații izolate unde nu dispunem de infrastructură de rețea Internet – în loc de temperatură și umiditate putem face achiziția unor parametrii ai solului (aplicații pentru agricultură inteligentă) sau achiziția unor parametrii de proximitate și integritate (aplicații de securitate).

Soluția propusă se bazează pe placa de dezvoltare Adafruit Feather 32U4 FONA, placă ce combină un microcontroler ATmega32U4 la 8MHz / 3.3V și un controler GSM SIM800 quad-band (850/900/1800/1900MHz). Placa necesită alimentarea de la un acumulator LiPo extern de minim 500mAh (se pot utiliza (*), (*), (*)) și o antenă GSM uFL.

2

Pentru achiziția parametrilor temperatură și umiditate vom utiliza un senzor digital I2C Si7021 ce are o precizie mare de măsurare și se poate interfața foarte ușor cu placa de dezvoltare utilizată.

3

Funcționarea sistemului necesită o cartelă GSM 2G cu capabilități de transfer de date. Pentru acest lucru vă propunem utilizarea unui SIM Simfony Mobile M2M – cartelă GSM ce oferă exclusiv servicii mobile de date.

4

Cartela este disponibilă gratuit prin comandă pe site-ul companiei Simfony Mobile SRL  sau împreună cu un produs din gama GSM pe site-ul Robofun. Cartela necesită înregistrarea și introducerea codului promoțional pentru activare și oferă gratuit 10MB de date mobile valabile 3 luni. Ulterior costurile de funcționare sunt de 0.25EURO, 0.5EURO, 1EURO pentru 1MB, 5MB respectiv 10MB trafic de date. Chiar dacă traficul inclus are valori modice pentru un sistem de raportare IoT este suficient iar costurile sunt rezonabile. O caracteristică importantă a cartelei SIM Simfony este indepența de un operator de telefonie anume, dispozitivul GSM ce utilizează cartela Simfony poate utiliza orice operator de telefonie mobilă în funcție de zona în care se află chiar și afara României.

 

Interconectarea componentelor

Interconectarea dintre senzorul Si7021 și placa Feather 32U4 FONA este reprezentată în diagrama următoare:

5

Pentru mai multe detalii despre operarea plăcii Adafruit Feather 32U4 FONA se recomandă parcurgerea materialului ”Adafruit Feather 32u4 FONA – Take your Feather anywhere in the world”.

 

Programarea sistemului

Pentru realizarea și testarea sistemului s-a utilizat Arduino IDE 1.8.1 cu extensia Adafruit AVR Boards 1.4.9 instalată precum și bibliotecile Adafruit FONA 1.3.2, Adafruit Si7021, Sleep_n0m1 1.1.1.

 

#include „Adafruit_FONA.h”

#define FONA_RX  9

#define FONA_TX  8

#define FONA_RST 4

#define FONA_RI  7

#define FONA_DTR 5

#define apn „internet.simfony.net”

#define apnusername „” 

#define apnpassword „” 

char replybuffer[255];

#include <SoftwareSerial.h>

SoftwareSerial fonaSS = SoftwareSerial(FONA_TX, FONA_RX);

SoftwareSerial *fonaSerial = &fonaSS;

Adafruit_FONA fona = Adafruit_FONA(FONA_RST);

uint8_t type;

#include <Sleep_n0m1.h>

Sleep sleep;

#include <Adafruit_Si7021.h>

Adafruit_Si7021 sensor = Adafruit_Si7021();

 

Dacă se dorește supravegherea funcționării sistemului pe serială se va adăuga următoarea linie, în funcționarea propriu-zisă nu este necesară:

 

#define debug

 

În cadrul secțiunii setup() se va realiza inițializarea modulului GSM și a senzorului Si7021:

 

void setup() {

  #ifdef debug

    while (!Serial);

    Serial.begin(115200);

    Serial.println(„Initializing…”);

  #endif

  digitalWrite(FONA_DTR,LOW);

  fonaSerial->begin(4800);

  if (! fona.begin(*fonaSerial)) {

    #ifdef debug

      Serial.println(F(„Couldn’t find FONA”));

    #endif

    delay(1);

    while (1);

  }

  #ifdef debug

    type = fona.type();

    Serial.println(F(„FONA is OK”));

    Serial.print(F(„Found „));

    switch (type) {

      case FONA800L:

        Serial.println(F(„FONA 800L”)); break;

      case FONA800H:

        Serial.println(F(„FONA 800H”)); break;

      case FONA808_V1:

        Serial.println(F(„FONA 808 (v1)”)); break;

      case FONA808_V2:

        Serial.println(F(„FONA 808 (v2)”)); break;

      case FONA3G_A:

        Serial.println(F(„FONA 3G (American)”)); break;

      case FONA3G_E:

        Serial.println(F(„FONA 3G (European)”)); break;

      default:

        Serial.println(F(„???”)); break;

    }

   #endif

  #ifdef debug

    char imei[15] = {0};

    uint8_t imeiLen = fona.getIMEI(imei);

    if (imeiLen > 0) {

      Serial.print(„Module IMEI: „); Serial.println(imei);

    }

  #endif

  fona.getSIMCCID(replybuffer); 

  #ifdef debug

    Serial.print(F(„SIM CCID = „));

Serial.println(replybuffer);

  #endif

  if (!fona.enableNetworkTimeSync(true)) {

     #ifdef debug

      Serial.println(F(„Failed to enable NTS”));

     #else

      ;

     #endif

  }

  delay(5000);

  fona.setGPRSNetworkSettings(F(apn),F(apnusername),F(apnpassword));

  uint8_t n=0;

  #ifdef debug

    Serial.print(„Connecting to network.”);

  #endif

  while (n!=5) {

    n = fona.getNetworkStatus();

    #ifdef debug

      Serial.print(„.”);

    #endif

    delay(1000);

  }

  #ifdef debug

    Serial.println(„OK”);

  #endif

  #ifdef debug

    n = fona.getRSSI();

    int8_t r;

    if (n == 0) r = -115;

    if (n == 1) r = -111;

    if (n == 31) r = -52;

    if ((n >= 2) && (n <= 30)) { r = map(n, 2, 30, -110, -54); }

    Serial.print(r); Serial.println(F(„dBm”));

  #endif

  sensor.begin();

  delay(5000);

}

 

Sistemul va raporta către serviciul IoT ThingSpeak valorile achiziționate (temperatură și umiditate) precum și voltajul și procentul de încărcare a acumulatorului ce alimentează sistemul. Secțiunea loop() implementează atât partea de achiziție cât și partea de transmisie de rețea prin intermediul comunicației mobile. În cadrul codului trebuie personalizată valoarea parametrului key ce se obține în urma înregistrării gratuite pe site-ul ThingSpeak. Achiziția și raportarea se realizează la un interval de o oră (3600000ms), în intervalul de inactivitate atât modulul GSM cât și microcontrolerul 32u4 se află în mod de consum redus.

 

void loop() {

while (!fona.enableGPRS(true)) {

#ifdef debug

Serial.println(F(„Failed to turn on GPRS”));

#endif

delay(5000);

}

uint16_t vbat;

uint16_t pbat;

#ifdef debug

if (fona.getBattVoltage(&vbat)) { Serial.print(F(„VBat = „)); Serial.print(vbat);

Serial.println(F(” mV”)); }

if (fona.getBattPercent(&pbat)) { Serial.print(F(„VPct = „)); Serial.print(pbat);

Serial.println(F(„%”));   }

Serial.println(„––––––––––––––––––„);

#else

fona.getBattVoltage(&vbat);

fona.getBattPercent(&pbat);

#endif

float temperature, humidity;

temperature = sensor.readTemperature();

humidity = sensor.readHumidity();

#ifdef debug

Serial.print(„Humidity:    „);

Serial.print(humidity, 2);

Serial.print(„\tTemperature: „);

Serial.println(temperature, 2);

#endif

String temp = „api.thingspeak.com/update?key=…&field1=” + String(vbat/1000.0F,2) + „&field2=” + String(pbat) + „&field3=” + String(temperature,2) + „&field4=” + String(humidity,2);

uint16_t statuscode;

int16_t length;

char url[100];

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

#ifdef debug

Serial.println(url);

if (!fona.HTTP_GET_start(url, &statuscode, (uint16_t *)&length)) Serial.println(„Failed

read HTTP!”);

#else

fona.HTTP_GET_start(url, &statuscode, (uint16_t *)&length);

#endif

while (length > 0) {

while (fona.available()) {

char c = fona.read();

#ifdef debug

Serial.write(c);

#endif

length–;

if (! length) break;

}

#ifdef debug

Serial.println();

#endif

break;

}

fona.HTTP_GET_end();

delay(100);

#ifdef debug

Serial.println(„––––––––––––––––––„);

if (!fona.enableGPRS(false)) Serial.println(F(„Failed to turn off GPRS”));

#else

fona.enableGPRS(false);

#endif

delay(100);

digitalWrite(FONA_DTR,HIGH);

#ifdef debug

delay(3600000);

#else

sleep.pwrDownMode();

sleep.sleepDelay(3600000);

#endif

digitalWrite(FONA_DTR,LOW);

delay(100);

}

 

Configurarea sistemului de raportare IoT

Sistemul nostru este un sistem tipic IoT ce utilizează platforma on-line ThingSpeak. Utilizarea platformei necesită înregistrare dar aceasta este gratuită.

6

Platforma ThingSpeak este una dintre cele mai cunoscute platforme IoT ce oferă servicii de stocare, prelucrare și vizualizare a datelor. Unul dintre avantajele majore ale platformei este posibilitatea de execuție de programe scrise în limbajul Matlab.

După înregistrare se va defini un nou canal înregistrare a datelor (My Channels / New Channel). Definirea canalului va genera și cheia (Write API Key) ce trebuie utilizată în program.

7

În cadrul acestui canal vom defini patru câmpuri Vbat, Vperc, Temperature și Humidity ce vor stoca efectiv datele trimise de dispozitivul nostru.

8

După punerea în funcțiune a sistemului se vor putea vedea și datele trimise de dispozitiv (secțiunea PrivateView).

9

IoT Power Monitor

Măsurarea consumului echipamentelor electronice este o preocupare continuă în domeniul monitorizării utilizării energiei electrice. Evoluția sistemelor IoT a făcut posibilă apariției unor echipamente de monitorizare a consumului ce raportează datele măsurate prin intermediul rețelei Internet direct către un serviciu cloud. În cadrul acestui proiect vom prezenta construcția unui astfel de sistem IoT de monitorizare a puterii electrice consumate.

Sistemul se va baza pe placa de dezvoltare LinkIt Smart 7688 Duo ce oferă o combinație extrem de puternică (asemănătoare și compatibilă software cu placa de dezvoltare Arduino Yun) între un microprocesor MediaTek MT7688 ce rulează sistemul de operare OpenWRT și un microcontroler ATmega32U4.

2

Pentru a utiliza conectivitatea de rețea oferită de componenta MT7688 este nevoie de configurarea conexiunii WiFi. Configurarea conexiunii WiFi necesită conectarea la AP-ul (Access Point) LinkIt_Smart_7688_XXXXXX (XXXXXX este un identificator specific fiecărei plăci în parte) cu ajutorul unui laptop sau un terminal inteligent WiFi (telefon inteligent, tabletă). După conectare se deschide cu ajutorul unui client web (browser) adresa http://192.168.100.1 sau http://mylinkit.local ce permite accesul la interfața de administrare a componentei MT7688 / a sistemului de operare OpenWRT. La prima conectare se va stabili și parola utilizatorului root (utilizator cu drepturi de administrator).

3

Modificările necesare conectării plăcii LinkIt Smart 7688 Duo la Internet presupun trecerea componentei WiFi din mod AP în mod client (Station mode) și configurarea conectării la un AP ce oferă conectivitate Internet. Ambele modificări se fac din secțiunea Network a interfeței de administrare.

4

5

Atenție!!! Nu introduceți placa de dezvoltare într-o subrețea 192.168.100.0/24 deoarece chiar și în modul client interfața AP cu adresa 192.168.100.1 este activă și o altă adresă IP din aceiași subrețea va deruta mecanismul de rutare TCP/IP.

După configurare accesului la Internet, placa se poate accesa prin intermediul interfeței web prezentată anterior sau prin intermediul protocolului SSH, utilizând IP-ul oferit de AP-ul configurat. Utilizatorul necesar conectării este root și parola stabilită anterior. Pentru mai mult informații legate de configurarea plăcii LinkIt Smart 7688 Duo se poate consulta și „Get Started with the LinkIt Smart 7688 Duo Development Board”.

A doua operație de configurare, necesară pentru a utiliza biblioteca Bridge (bibliotecă specifică plăcii Arduino Yun) sub mediul Arduino IDE, necesită conectarea la consola plăcii de dezvoltare LinkIt Smart 7688 Duo prin intermediul unui client SSH (putty (3), de exemplu) și executarea următoarelor comenzi:

uci set yunbridge.config.disabled=’0′ 

uci commit 

reboot

Pentru măsurarea consumului vom utiliza senzorul de curent INA219 – senzor digital I2C ce permite măsurarea de tensiuni de până la 26V și curenți de până la 3.2A cu o precizie de 1%. Interconectarea cu placa de dezvoltare se va face prin intermediul magistralei I2C:

6

Senzorul se va alimenta la 3.3V iar pinii SCL și SDA se vor conecta la pinii D3 respectiv D2 ai plăcii de dezvoltare. Pentru a putea măsura curentul și tensiunea este necesar să trecem linia de alimentare a dispozitivului electronic monitorizat prin senzor. Acest lucru se poate face prin secționarea firului de alimentare sau prin conectarea unei perechi de mufe jack mamă – tată care să permită conectarea mufei de alimentare la senzor și senzorul la dispozitivul electronic. ATENȚIE!!! Inserarea senzorului se face pe linia de tensiune nu pe cea de masă.

Având în vedere scopul de monitorizare IoT a sistemului realizat în cadrul lecției de față vom utiliza cloud Robofun IoT. Pentru utilizarea acestui serviciu este necesară înregistrarea gratuită.

4

După înregistrare și conectare este necesară definirea unui senzor (Adauga senzor) pentru a putea înregistra puterea măsurată.

5

După definirea senzorului este necesar să copiem cheia de autentificare (Token) pentru a o utiliza în program.

3

Programul a fost realizat și testat utilizând Arduino IDE 1.8.3 cu extensia LinkIt Smart 7688 Duo 0.1.8 instalată și biblioteca Adafruit INA219 1.0.0.

#include <Bridge.h>

#include <HttpClient.h>

#include <Wire.h>

#include <Adafruit_INA219.h>

Adafruit_INA219 ina219;

Decomentarea directivei #define debug va permite supravegherea mesajelor de funcționare în consola serială.

//#define debug

void setup(void) {

  #ifdef debug

    SerialUSB.begin(115200);

    while (!SerialUSB);

  #endif

  ina219.begin();

  Bridge.begin();

}

În cadrul secțiunii loop() este necesară completarea valorii TOKEN obținută în procesul de înregistrare a senzorului în platforma Robofun IoT.

void loop(void) {

  float shuntvoltage = 0;

  float busvoltage = 0;

  float current_mA = 0;

  float loadvoltage = 0;

  float power = 0;

  shuntvoltage = ina219.getShuntVoltage_mV();

  busvoltage = ina219.getBusVoltage_V();

  current_mA = ina219.getCurrent_mA();

  loadvoltage = busvoltage + (shuntvoltage / 1000);

  power = loadvoltage * current_mA;

  #ifdef debug

    SerialUSB.print(„Bus Voltage:   „);

   SerialUSB.print(busvoltage);

   SerialUSB.println(” V”);

    SerialUSB.print(„Shunt Voltage: „);

    SerialUSB.print(shuntvoltage);

   SerialUSB.println(” mV”);

    SerialUSB.print(„Load Voltage:  „);

   SerialUSB.print(loadvoltage);

   SerialUSB.println(” V”);

    SerialUSB.print(„Current:       „);

   SerialUSB.print(current_mA); 

   SerialUSB.println(” mA”);

    SerialUSB.print(„Power:       „);

   SerialUSB.print(power);

   SerialUSB.println(” mW”);

    SerialUSB.println(„”);

  #endif

  HttpClient client;

  String temp = „http: ” + „//iot.robofun.ro/api/v1/senzor/TOKEN/input?value=” + String(power,2);

  client.get(temp);

  while (client.available()) {

    char c = client.read();

    #ifdef debug

      SerialUSB.print(c);

    #endif

  }

  #ifdef debug

    SerialUSB.println();

    SerialUSB.flush(); 

  #endif

  delay(60000);

}

Programul va trimite la un interval de 1 minut (60 secunde = 60000 milisecunde) valoarea calculată (puterea instantanee) pe baza curentului și tensiunii măsurate.

Testele au fost realizate monitorizând un dispozitiv de tip router WiFi HUAWEI HG8247H alimentat la 12V DC (curent continuu).

Graficul de valori înregistrate pe ultima zi (toate datele) – se poate observa un consum constant de minim 6600mW (6.6W):

7

Graficul de valori înregistrate pe ultima săptămână (cu mediere) – se poate observa o variație a consumului între 6619mW și 6800mW:

8

My Heart Will Go IoT

Măsurarea pulsului, a numărului de bătăi ale inimii pe minut, este un instrument de evaluare atât a stării de sănătate dar și a stării emoționale sau a efortului fizic depus. Apariția de senzori optici performanți fac această evaluare foarte simplă permițând măsurarea pulsului cu telefonul mobil sau cu diverse dispozitive de tip brățară. În cadrul proiectului de față vom explica construirea unui astfel de dispozitiv dar, în plus față de  dispozitivele clasice de măsurare a pulsului, vom înregistra datele obținute în cloud.

Atenție!!! Dispozitivul prezentat nu este un dispozitiv medical! Dispozitivul din acest proiect nu poate fi folosit pentru evaluarea stării de sănătate! Proiectul de față prezintă un principiu de măsurare și înregistrare nu și o metodă exactă de calibrare a datelor măsurate.

Pentru implementarea dispozitivului vom utiliza o placă de dezvoltarea Adafruit Feather M0 WiFi și un senzor de particule și puls MAX30105. Schema de interconectare dintre cele două componente este următoarea:

2

Senzorul comunică cu placa de dezvoltare prin intermediul magistralei I2C și atunci conexiunile dintre cele două componente sunt evidente: pinul SCL al senzorului la pinul 4 (SCL) al plăcii de dezvoltare și pinul SDA al senzorului la pinul 3 (SDA) al plăcii de dezvoltare. Pinul /INT al senzorului nu este folosit. Alimentarea senzorului se va face la 5V – pinul 5V se va conecta la pinul USB al plăcii de dezvoltare atâta timp cât placa se alimentează prin intermediul cablului USB. Dacă doriți să transformați sistemul într-un sistem portabil, alimentând placa de la un acumulator LiPo, atunci pinul de 5V al senzorului se va conecta la pinul BAT al plăcii de dezvoltare – senzorul se va alimenta la 3.7V (producătorul specifică posibilitatea de a alimenta senzorul la tensiuni între 3.3V și 5V dar explică că o tensiune de peste 3.5V va asigura o funcționare mai bună a senzorului).

Pentru punerea în funcțiune și pentru integrarea plăcii de dezvoltare cu mediul Arduino IDE este necesară parcurgerea materialului „Adafruit Feather M0 WiFi with ATWINC1500 – ARM Cortex M0+ with fast & fun wireless built in”. Pentru înțelegerea mai bună a modului de funcționare a senzorului de puls se poate parcurge materialul „MAX30105 Particle and Pulse Ox Sensor Hookup Guide”.

Programul sistemului se bazează pe exemplul Example5_HeartRate al bibliotecii software Sparkfun MAX3010x Pulse and Proximity Sensor Library. Programul a fost testat utilizând Arduino IDE 1.8.3 cu extensia Adafruit SAMD Boards 1.0.17 instalată și bibliotecile WiFi101 0.14.3 și Sparkfun MAX3010x 1.0.0. Atenție!!! Pentru a compila programul pentru placa Feather M0 este necesară adăugarea unei linii (#define BUFFER_LENGTH 32) în fișierul MAX30105.cpp al bibliotecii Sparkfun MAX3010x.

#include <SPI.h>

#include <WiFi101.h>

#define WINC_CS   8

#define WINC_IRQ  7

#define WINC_RST  4

#define WINC_EN   2 

Datele de conectare WiFi trebuie personalizate în program. Programul va înregistra datele măsurate în cloud utilizând serviciul Robofun IoT.

char ssid[] = „„;

char pass[] = „„; 

WiFiClient client;

int status = WL_IDLE_STATUS;

char server[] = „iot.robofun.ro”;

Constanta postingInterval va dicta intervalul (în milisecunde) la care se face înregistrarea datelor în cloud.

unsigned long lastConnectionTime = 0;

const unsigned long postingInterval = 30L * 1000L;

#include <Wire.h>

#include „MAX30105.h”

#include „heartRate.h”

MAX30105 particleSensor;

const byte RATE_SIZE = 4;

byte rates[RATE_SIZE];

byte rateSpot = 0;

long lastBeat = 0;

float beatsPerMinute;

int beatAvg;

Decomentarea directivei debug va permite vizualizarea datelor măsurate în concola serială.

//#define debug

3

În cadrul secțiunii setup() se va inițializa conexiunea WiFi și comunicația cu senzorul de puls. Mesajele de eroare vor apărea în consola serială doar dacă este activată directiva debug.

void setup() {

  #ifdef debug

    Serial.begin(115200);

    while (!Serial) { ; }

    Serial.println(„Initializing…”);

  #endif

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

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

    #ifdef debug

      Serial.println(„WiFi shield not present”);

    #endif

    while (true);

  }

  while (status != WL_CONNECTED) {

    #ifdef debug

      Serial.print(„Attempting to connect to SSID: „);

      Serial.println(ssid);

    #endif

    status = WiFi.begin(ssid, pass);

    delay(10000);

  }

  #ifdef debug

    Serial.println(„Connected to wifi”);

  #endif

  if (!particleSensor.begin(Wire, I2C_SPEED_FAST))

  {

    #ifdef debug

      Serial.println(„MAX30105 was not found. Please check wiring/power. „);

    #endif

    while (1);

  }

  #ifdef debug

    Serial.println(„Place your index finger on the sensor with steady pressure.”);

  #endif

  particleSensor.setup();

  particleSensor.setPulseAmplitudeRed(0x0A);

  particleSensor.setPulseAmplitudeGreen(0);

}

În cadrul secțiunii loop() se va efectua atât citirea senzorului de puls (BPM – bătăi pe minut, medie – Avg BPM) cât și postarea către serviciul Robofun IoT. În cadrul codului trebuie personalizată cheia de autentificare (TOKEN) primită la înregistrarea senzorului în cadrul serviciului IoT. Intervalul de postare se contorizează începând cu detecția degetului pe senzor, dacă se ia degetul de pe senzor nu se mai efectuează înregistrarea valorilor în cloud. Se recomandă utilizarea unui elastic pentru a asigura o presiune constantă pe senzor.

4

void loop() {

  long irValue = particleSensor.getIR();

  if (checkForBeat(irValue) == true)

  {

    long delta = millis() – lastBeat;

    lastBeat = millis();

    beatsPerMinute = 60 / (delta / 1000.0);

    if (beatsPerMinute < 255 && beatsPerMinute > 20) {

      rates[rateSpot++] = (byte)beatsPerMinute;

      rateSpot %= RATE_SIZE;

      beatAvg = 0;

      for (byte x = 0 ; x < RATE_SIZE ; x++)

        beatAvg += rates[x];

      beatAvg /= RATE_SIZE; }

  }

  #ifdef debug

    Serial.print(„IR=”);

    Serial.print(irValue);

    Serial.print(„, BPM=”);

    Serial.print(beatsPerMinute);

    Serial.print(„, Avg BPM=”);

    Serial.print(beatAvg);

  #endif

  if (irValue < 50000) {

    #ifdef debug

      Serial.print(” No finger?”);

    #endif

    lastConnectionTime = millis();

  }

  #ifdef debug

    Serial.println();

  #endif

  if ((millis() – lastConnectionTime > postingInterval) && (beatAvg > 20))

  {   String temp = „GET /api/v1/senzor/TOKEN/input?value=” + String(beatAvg) + ” HTTP/1.1″;

      char param[100];

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

      if (client.connect(server, 80)) {

        client.println(param);

        client.println(„Host: iot.robofun.ro”);

        client.println(„Connection: close”);

        client.println();

      }

      client.stop();

      lastConnectionTime = millis();

      lastBeat = 0;

  }

}

Pentru a utiliza serviciul Robofun IoT este necesară înregistrarea gratuită.

4

După înregistrare și conectare este necesară definirea unui noi senzor (Adauga senzor).

5

După definirea senzorului este necesar să copiem cheia de autentificare (Token) pentru a o utiliza în program.

3

După configurarea serviciului IoT și încărcarea programului pe placa de dezvoltare putem începe utilizarea dispozitivului și putem observa datele înregistrate on-line:

5

O facilitate foarte interesantă a serviciului Robofun IoT este posibilitatea de partajare a graficelor (Share senzor) sau de integrare a acestora în alte pagini web. Putem astfel să adăugăm paginii web personale sau blog-ului personal un grafic live cu numărul de bătăi ale propriei inimi.

6

Proiect Blynk IoT

Sparkfun Blynk ESP8266 este o placă de dezvoltare care, pe lângă avantajele oferite de serviciul cloud Blynk (prezentate în cadrul proiectului Cum să realizăm un sistem IoT fără să scriem nici o linie de cod?), oferă o combinație de componente extrem de interesante.

1

Procesorul WiFi ESP8266, senzorul digital de temperatură și umiditate Si7021 integrat și posibilitatea de alimentare de la un acumulator fac din placa Sparkfun Blynk ESP8266 o excelentă platformă pentru experimente IoT. În cadrul proiectului de față vom programa această placă utilizând Arduino IDE și nu vom utiliza platforma cloud specifică ci noul serviciu Robofun IoT.

2

Pentru punerea în funcțiune și programarea plăcii Blynk se recomandă parcurgerea materialului Blynk Board Arduino Development Guide. Pentru prima parte a proiectului nu avem nevoie decât de placa de dezvoltare și cablul USB de încărcare a programului – senzorul de temperatură și umiditate ce va furniza informațiile transmise către serviciul IoT este integrat pe placă.

Programul a fost dezvoltat și testat utilizând Arduino IDE 1.8.1, extensia esp8266 2.1.0 și bibliotecile Sparkfun Si7021 Humidity and Temperature Sensor 1.0.0 și Adafruit NeoPixel 1.1.1 (pe placa de dezvoltare se află și un led RGB WS2812 pe care îl vom comanda utilizând această bibliotecă). Declarațiile inițiale ale programului sunt următoarele:

#include <Wire.h>
#include „SparkFun_Si7021_Breakout_Library.h”
#include <Adafruit_NeoPixel.h>
#include <ESP8266WiFi.h>
#include <ESP8266HTTPClient.h>

În cadrul programului trebuie personalizate datele de conectare WiFi:

char ssid[] = „„;

char pass[] = „„;

WiFiClient client;

float humidity = 0;

float tempC = 0;

Weather sensor;

#define PIN  4

#define NUMPIXELS 1

Adafruit_NeoPixel pixels = Adafruit_NeoPixel(NUMPIXELS, PIN, NEO_GRB + NEO_KHZ800);

Varialbila postingInterval indică intervalul de timp între două raportări IoT (în milisecunde, în cazul dat ca exemplu: 10 minute)

unsigned long lastConnectionTime = 0;

const unsigned long postingInterval = 600L * 1000L;

Dacă doriți ca programul să afișeze informații despre execuție se va decomenta următoarea linie:

//#define debug

3

În cadrul secțiunii setup() se va inițializa conexiunea WiFi – pe durata inițializării led-ul RGB de pe placă va lumina roșu.

void setup() {

  pixels.begin();

  pixels.setPixelColor(0, pixels.Color(150,0,0));

  pixels.show();

  #ifdef debug

    Serial.begin(9600);

  #endif

  int status = WL_IDLE_STATUS;

  while (status != WL_CONNECTED)

  {

    status = WiFi.begin(ssid, pass);

    delay(10000);

  }

  #ifdef debug

    Serial.println(„Connected to wifi”);

  #endif

  sensor.begin();

  pixels.setPixelColor(0, pixels.Color(0,0,0));

  pixels.show();

}

Secțiunea loop() va verifica timpul scurs de la ultima raportare și dacă este depășit intervalul specificat se va apela procedura IoTPublish() care se ocupă de citirea senzorului Si7021 și de trimiterea datelor către Robofun IoT. Pe durata execuției procedurii led-ul de pe placă va lumina verde.

void loop() {

  if (millis() – lastConnectionTime > postingInterval)

  {

    pixels.setPixelColor(0, pixels.Color(0,150,0));

    pixels.show();

    IoTpublish();

    pixels.setPixelColor(0, pixels.Color(0,0,0));

    pixels.show();

  }

}

Procedura IoTpublish() va posta datele oferite de senzorul de temperatură și umiditate către serviciul IoT. Postarea se face prin două apeluri HTTP GET, câte unul pentru fiecare parametru trimis (temperatură și umiditate). Apelurile HTTP vor returna răspunsul 1 dacă totul este în regulă sau alte valoare dacă a apărut o eroare. Pentru utilizarea serviciului Robofun IoT este necesară înregistrarea gratuită a unui cont.

4

După înregistrare și conectare este necesară definirea a doi noi senzori.

5

Fiecare senzor va avea propria cheie de autentificare (token) și pentru fiecare dintre cei doi senzori este necesar să copiem această cheie în program (SENZOR_TOKEN1 și SENZOR_TOKEN2).

void IoTpublish() {

  String SENSOR_TOKEN1=”„;

  String SENSOR_TOKEN2=”„;

  HTTPClient http;

  humidity = sensor.getRH();

  tempC = sensor.getTemp();

  String data =

    „http” + „://iot.robofun.ro/api/v1/senzor/” + SENSOR_TOKEN1 + „/input?value=” + String(tempC, DEC);

  http.begin(data);

  int httpCode = http.GET();

  #ifdef debug

    if(httpCode > 0) {

      Serial.printf(„[HTTP] GET… code: %d\n”, httpCode);

      if(httpCode == HTTP_CODE_OK) {

        String payload = http.getString();

        Serial.println(payload);

      }

    } else {

      Serial.printf(„[HTTP] GET… failed, error: %s\n”, http.errorToString(httpCode).c_str());

    }

  #endif

  http.end();

  data = „http” + „://iot.robofun.ro/api/v1/senzor/” + SENSOR_TOKEN2 + „/input?value=” + String(humidity, DEC);

  http.begin(data);

  httpCode = http.GET();

  #ifdef debug

    if(httpCode > 0) {

      Serial.printf(„[HTTP] GET… code: %d\n”, httpCode);

      if(httpCode == HTTP_CODE_OK) {

        String payload = http.getString();

        Serial.println(payload);

      }

    } else {

      Serial.printf(„[HTTP] GET… failed, error: %s\n”, http.errorToString(httpCode).c_str());

    }

  #endif

  http.end();

  lastConnectionTime = millis();

}

După încărcarea programului pe placa de dezvoltare și scurgerea unui interval de funcționare mai lung decât postingInterval vom putea vedea deja pe graficul oferit de interfața serviciului valorile achiziționate de senzorul plăcii. În capturile de ecran următoare putem vedea un grafic pe o perioadă mai lungă de timp (temperatura – vizualizare a tuturor valorilor, umiditate – vizualizare a valorilor medii la nivel de săptămână).

6

7

Un atu foarte important al plăcii de dezvoltare Sparkfun Blynk ESP8266 este posibilitatea de alimentare utilizând un acumulator LiPo de 3.7V. Asta conferă sistemului portabilitate – vom dispune de un sistem de achiziție IoT de dimensiunea a două cutii de chibrituri. Totuși, o problemă majoră care se naște în acest caz este evaluarea autonomiei de funcționare a sistemului – determinarea nivelului de încărcare a acumulatorului. Utilizarea convertorului analog-numeric al plăcii în acest scop presupune construirea unui divizor de tensiune (convertorul poate măsura tensiuni între 0V și 3.3V) dar această soluție conduce la descărcarea mai rapidă a acumulatorului. O soluție mult mai elegantă pentru rezolvarea acestei probleme este utilizarea unei componente specializate de măsurare: LiPo Fuel Gauge. Conectarea acestei componente, între conectorul de alimentare al plăcii și acumulator, este prezentată în diagrama următoare:

8

Circuitul integrat MAX17043G+U al componentei de măsurare va supraveghea nivelul de tensiune al acumulatorului și îl va raporta către placa de dezvoltarea prin intermediul magistralei I2C – placa de dezvoltare are un conector special I2C la care se poate conecta componenta de măsurarea. Introducerea componentei de măsurare nu va influența în nici un fel posibilitatea de încărcarea a acumulatorului prin intermediul mufei microUSB de pe placă și nici nu va scădea durata de viață a acumulatorului.

În cadrul programului vom adăuga următoarele funcții și proceduri necesare lucrului cu circuitul MAX17043:

unsigned int vcellMAX17043() {

unsigned int vcell;

vcell = i2cRead16(0x02);

vcell = vcell >> 4;

return vcell; }

float percentMAX17043() {

unsigned int soc;

float percent;

soc = i2cRead16(0x04);

percent = (byte) (soc >> 8);

percent += ((float)((byte)soc))/256;

return percent; }

void qsMAX17043() {

i2cWrite16(0x4000, 0x06); }

unsigned int i2cRead16(unsigned char address) {

int data = 0;

Wire.beginTransmission(MAX17043_ADDRESS);

Wire.write(address);

Wire.endTransmission();

Wire.requestFrom(MAX17043_ADDRESS, 2);

while (Wire.available() < 2) ;

data = ((int) Wire.read()) << 8;

data |= Wire.read();

return data; }

void i2cWrite16(unsigned int data, unsigned char address) {

Wire.beginTransmission(MAX17043_ADDRESS);

Wire.write(address);

Wire.write((byte)((data >> 8) & 0x00FF));

Wire.write((byte)(data & 0x00FF));

Wire.endTransmission(); }

La începutul programului vom defini adresa I2C a circuitului MAX17043:

#define MAX17043_ADDRESS 0x36

Iar în cadrul procedurii IoTpublish() vom adăuga:

float batVoltage;

float batPercentage;

String SENSOR_TOKEN3=”…”;

String SENSOR_TOKEN4=”…”;

….

qsMAX17043();

batPercentage = percentMAX17043();

batVoltage = (float) vcellMAX17043() * 1/800;

data = „http” + „://iot.robofun.ro/api/v1/senzor/” + SENSOR_TOKEN3 + „/input?value=” + String(batVoltage, DEC);

http.begin(data);

httpCode = http.GET();

#ifdef debug

if(httpCode > 0) {

Serial.printf(„[HTTP] GET… code: %d\n”, httpCode);

if(httpCode == HTTP_CODE_OK) {

String payload = http.getString();

Serial.println(payload);

}

} else {

Serial.printf(„[HTTP] GET… failed, error: %s\n”, http.errorToString(httpCode).c_str());

}

#endif

http.end();

data = „http” + „://iot.robofun.ro/api/v1/senzor/” + SENSOR_TOKEN4 + „/input?value=” + String(batPercentage, DEC);

http.begin(data);

httpCode = http.GET();

#ifdef debug

if(httpCode > 0) {

Serial.printf(„[HTTP] GET… code: %d\n”, httpCode);

if(httpCode == HTTP_CODE_OK) {

String payload = http.getString();

Serial.println(payload); }

} else {

Serial.printf(„[HTTP] GET… failed, error: %s\n”, http.errorToString(httpCode).c_str());   }

#endif

http.end();

Înainte de încărcarea noii versiuni de program este necesar să definim doi noi senzori (unul pentru tensiunea acumulatorului altul pentru procentul de încărcare) și să copiem cheile de autentificare în variabilele asociate: SENZOR_TOKEN3 și SENZOR_TOKEN4. După rularea programului vom putea vedea evoluția parametrilor acumulatorului folosit:

9

10