Touchless Heart

În cadrul acestui proiect vom exemplifica utilizarea senzorului APDS-9960. Acest senzor poate fi utilizat în aplicații diverse de detecție a luminii, a culorilor, a proximității dar și a gesturilor. Detecția gesturilor utilizatorului este funcționalitatea inedită oferită de senzorul APDS-9960. Această funcționalitate o vom utiliza pentru a controla prin gesturi o inimă de hârtie semitransparentă în care vom introduce un LED brick RGB. La pornirea sistemului LED-ul va pulsa roșu. Dacă vom apropria mâna de montaj LED-ul va pulsa din ce în ce mai rapid sugerând  emoție. Dacă vom depărta mâna LED-ul își va schima culoare din roșu în verde sugerând dezamăgire. Există 6 gesturi ce pot fi captate de senzor: apropierea și depărtarea mâinii, mișcare la dreapta, stânga, sus și jos. Având în vedere acest lucru, sistemul propus poate fi complicat mai ales că LED-ul utilizat poate genera o multitudine de culori și moduri de funcționare. Puteți crea o poveste proprie în care să îmbinați gesturi, culori și o „inimă de neatins”.

Pentru implementarea algoritmului de funcționare vom utiliza o placă de dezvoltare Arduino Uno dar poate fi utilizată orice placă Arduino. Conectarea dintre senzor și placa de dezvoltare se va face utilizând magistrala I2C, schema de interconectare este următoarea:

3

Pinul INT al senzorului se va conecta la pinul D2 al plăcii de dezvoltare, SCL la A5, SDA la A4, VCC la 3.3V și GND la GND. Pentru mai multe informații despre funcționarea senzorului APDS-9960 puteți consulta: „APDS-9960 RGB and Gesture Sensor Hookup Guide”.

Brick-ul LED RGB se conectează la placa de dezvoltare prin intermediul pinilor D5 (red), D6 (green) și D9 (blue) – toți cei trei pini ai plăcii de dezvoltare au capabilitatea de a genera semnal PWM adică puteți folosi și efecte de aprindere parțială a oricărei dintre cele trei culori (roșu, verde, albastru).

Programul sistemului este derivat din exemplul GestureTest.ino al bibliotecii Sparkfun APDS-9960 RGB and Gesture Sensor. Programul a fost testat utilizând Arduino IDE 1.8.5 și biblioteca Sparkfun APDS-9960 RGB and Gesture Sensor 1.4.2. În consola serială se va afișa interpretarea gesturilor recunoscute de senzor (FAR, NEAR, UP, DOWN, LEFT, RIGHT) chiar dacă pentru comanda inimii sunt folosite doar două (FAR și NEAR). NONE semnifică nerecunoaștere gestului.

4

#include <Wire.h>

#include <SparkFun_APDS9960.h>

#define APDS9960_INT    2

#define redPin   5

#define greenPin 6

#define bluePin 9

SparkFun_APDS9960 apds = SparkFun_APDS9960();

int isr_flag = 0;

byte r,g,b;

int beat;

void setup() {

  pinMode(APDS9960_INT, INPUT);

  pinMode(redPin, OUTPUT);

  pinMode(greenPin, OUTPUT);

  pinMode(bluePin, OUTPUT);

  Serial.begin(9600);

  Serial.println();

  Serial.println(F(“——————————–“));

  Serial.println(F(“Touchless Heart – Start beating…”));

  Serial.println(F(“——————————–“));

  attachInterrupt(0, interruptRoutine, FALLING);

  if ( apds.init() ) {

    Serial.println(F(“APDS-9960 initialization

complete”));

  } else {

    Serial.println(F(“Something went wrong during APDS-

9960 init!”));

  }

  if ( apds.enableGestureSensor(true) ) {

    Serial.println(F(“Gesture sensor is now running”));

  } else {

    Serial.println(F(“Something went wrong during gesture

sensor init!”));

  }

  r=255;

  g=0;

  b=0;

  beat = 1000;

}

Programul pornește cu un beat (interval de timp aprins / stins) de 1000 ms (1 secundă). La fiecare semn de apropriere (NEAR) recunoscut din acesta se scade 100 ms. La semnul de îndepărtare (FAR) intervalul revine la 1000 ms.

void loop() {

  if( isr_flag == 1 ) {

    detachInterrupt(0);

    handleGesture();

    isr_flag = 0;

    attachInterrupt(0, interruptRoutine, FALLING);

  }

  color(r,g,b);

  delay(beat);

  color(0,0,0);

  delay(beat);

}

void color(byte red, byte green, byte blue) {

  analogWrite(redPin, red);

  analogWrite(greenPin, green);

  analogWrite(bluePin, blue);

}

void interruptRoutine() { isr_flag = 1; }

void handleGesture() {

    if ( apds.isGestureAvailable() ) {

    switch ( apds.readGesture() ) {

      case DIR_UP:

        Serial.println(“UP”);

           break;

      case DIR_DOWN:

        Serial.println(“DOWN”);

        break;

      case DIR_LEFT:

        Serial.println(“LEFT”);

        break;

      case DIR_RIGHT:

        Serial.println(“RIGHT”);

        break;

      case DIR_NEAR:

        Serial.println(“NEAR”);

        r=255;

        g=0;

        beat-=100;

        break;

      case DIR_FAR:

        Serial.println(“FAR”);

        r=0;

        g=255;

        beat = 1000;

        break;

      default:

        Serial.println(“NONE”);

    }

  }

}

Vă recomandăm și un proiect interesant ce utilizează senzorul APDS-9960 pentru a detecta culorile: „Arduino Color Differentiate /with Apds-9960”.

Cum să utilizăm corect senzorul BME280

BME280 este un senzor digital integrat ce permite măsurarea temperaturii, umidității și presiunii atmosferice. Utilizat cu precădere în dispozitive mobile, datorită dimensiunii reduse și a consumului mic, senzorul BME280 oferă performanțe bune la un preț scăzut. Din acest motiv acest senzor este extrem de popular și este folosit în multe proiecte ce utilizează plăci de dezvoltare Arduino, ESP8266 sau Raspberry Pi. Bineînțeles, formatul LGA face ca senzorul să fie dificil de integrat în scheme electronice personale dar, din fericire, există disponibile componente brick / breakout ce permit conectarea simplă la o placă de dezvoltare în montaje bazate pe breadboard și fire de interconectare (de exemplu, SparkFun Atmospheric Sensor Breakout – BME280).

1

Pentru comunicația cu o placă de dezvoltare senzorul permite conectarea prin intermediul magistralei I2C sau, alternativ, prin intermediul magistralei SPI. Având în vedere faptul că senzorul funcționează la 3.3V, conectarea prin intermediul magistralei I2C se poate face fără probleme cu o placă ce funcționează la 5V (Arduino Uno, Arduino Leonardo) dar, în cazul magistralei SPI, necesită adaptor a nivelurilor logice 3.3V <-> 5V. Pentru plăcile ce funcționează la 3.3V (Adafruit Feather, NodeMCU, Raspberry Pi) nu există nici un fel de problemă. Pentru mai multe detalii legate de conectarea senzorului la diverse plăci de dezvoltare se poate parcurge materialul „SparkFun BME280 Breakout Hookup Guide”.

3

Ușurința conectării și disponibilitatea unei biblioteci de lucru cu acest senzor ne face să ignorăm complexitatea funcționării acestuia. De cele mai multe ori preluăm codul dat ca exemplu în cadrul bibliotecii și nu alocăm timpul necesar înțelegerii modului de funcționare. Montajele vor funcționa dar vom utiliza corect senzorul? Să luăm ca exemplu programul I2C_ReadAllData.ino din cadrul bibliotecii Sparkfun BME280. În cadrul acestui exemplu în partea de inițializare se realizează următoarele setări, preluate de cele mai multe proiecte ca atare:

   mySensor.settings.runMode = 3;

    mySensor.settings.tStandby = 0;

    mySensor.settings.filter = 0;

    mySensor.settings.tempOverSample = 1;

    mySensor.settings.pressOverSample = 1;

    mySensor.settings.humidOverSample = 1;

Ce reprezintă aceste setări? Sunt potrivite pentru orice proiect?

runMode – configurează modul de funcționare al senzorului. Există trei moduri de funcționare: Sleep mode – senzorul nu efectuează nici o operație dar registrele interne sunt accesibile (mySensor.settings.runMode = 0;); Forced mode – senzorul efectuează o singură citire și revine în starea de Sleep mode (mySensor.settings.runMode = 1 sau 2;); Normal mode – senzorul efectuează o succesiune continuă de citiri (mySensor.settings.runMode = 3;). Sleep mode este modul în care se află senzorul după pornire. În funcție de domeniul de utilizare se poate configura în Forced mode sau în Normal mode.

tStandby – reprezintă timpul între două achiziții succesive în Normal mode. Chiar dacă din program noi citim la un interval de câteva secunde, minute sau ore, intern senzorul realizează achiziția la intervale de: 0, 0.5ms; 1, 62.5ms; 2, 125ms; 3, 250ms; 4, 500ms; 5, 1000ms; 6, 10ms; 7, 20ms;

filter – numărul de coeficienți utilizați în filtrarea parametrilor achiziționați. Poate avea valorile: 0 – nu se aplică filtru,  1– 2 coeficienți, 2 – 4 coeficienți, 3 – 8 coeficienți, 4 – 16 coeficienți. Procesul de filtrare asigură stabilitatea procesului de achiziție rejectând fluctuațiile generate de perturbații, mai ales pentru temperatură și presiune atmosferică, crește rezoluția dar crește și timpul de achiziție.

tempOverSample, pressOverSample, humidOverSamplenumărul de eșantioane preluate la o singură citire pentru parametrii temperatură, presiune atmosferică și umiditate. Poate avea valorile: 0 – nu se efectuează oversampling, de la 1 la 5 pentru 1, 2, 4, 8, 16 eșantioane. Opțiune utilă pentru reducere zgomotelor în procesul de achiziție.

În manualul oficial al senzorului se dau următoarele scenarii de utilizare a acestor parametrii:

Sistem de monitorizare a vremii (curent consumat de senzor aproximativ 0.16 µA)

Forced mode

Oversampling temperatură, umiditate, presiune: 1

Filter off

mySensor.settings.runMode = 1;

mySensor.settings.filter = 0;

mySensor.settings.tempOverSample = 1;

mySensor.settings.pressOverSample = 1;

mySensor.settings.humidOverSample = 1;

Sistem de monitorizare a umidității (curent consumat de senzor aproximativ 2.9 µA)

Forced mode

Oversampling temperatură, umiditate:1, presiune: 0

Filter off

mySensor.settings.runMode = 1;

mySensor.settings.filter = 0;

mySensor.settings.tempOverSample = 1;

mySensor.settings.pressOverSample = 0;

mySensor.settings.humidOverSample = 1;

Sistem de navigare în interior – precizie mare în determinarea altitudinii (curent consumat de senzor aproximativ 633 µA)

Normal mode, tStanby = 0.5ms

Oversampling temperatură:1, umiditate:0, presiune: 16

Filter coefficient 16

mySensor.settings.runMode = 3;

mySensor.settings.tStandby = 0;

mySensor.settings.filter = 5;

mySensor.settings.tempOverSample = 1;

mySensor.settings.pressOverSample = 4;

mySensor.settings.humidOverSample = 0;

Sistem gaming – răspuns rapid la modificarea altitudinii (curent consumat de senzor aproximativ 581 µA)

Normal mode, tStanby = 0.5ms

Oversampling temperatură:1, umiditate:0, presiune: 4

Filter coefficient 16

mySensor.settings.runMode = 3;

mySensor.settings.tStandby = 0;

mySensor.settings.filter = 5;

mySensor.settings.tempOverSample = 1;

mySensor.settings.pressOverSample = 2;

mySensor.settings.humidOverSample = 0;

În funcție de destinația sistemului în care utilizați senzorul BME280 puteți ajusta acești parametrii pentru a atinge performanțele dorite. Nu utilizați modul Normal pentru un sistem ce efectuează citiri la intervale mari de timp, modul Forced asigură un consum mult mai mic. Pentru citiri rapide nu utilizați un număr mare de oversampling. Dacă aveți nevoie doar de unul sau doi parametrii din cei trei furnizați de senzor puteți dezactiva parametrul nefolositor prin atribuirea valorii 0 la oversampling scăzând consumul și crescând viteza de achiziție. Pentru acuratețe mare în achiziție utilizați un număr mare de coeficienți de filtrare.

Solar Power GPRS Test

Întrebarea la care vom încerca să răspundem în cadrul acestui proiect este: putem construi un sistem IoT cu comunicație GPRS total independent de alimentarea de la rețeaua de energie? Sistemul propus pentru testare este un sistem ce se bazează exclusiv pe alimentare solară. Un astfel de sistem poate fi folosit pentru locații izolate în care nu există disponibile alimentare cu energie electrică și Internet.

Ca soluție de alimentare solară vom utiliza o componentă MPPT: Sunny Buddy – MPPT Solar Charger. Această componentă permite conectarea unei celule solare cu ieșirea între 6V și 20V și încărcarea unui acumulator LiIon sau LiPo cu o singură celulă (3.7V). Curentul maxim de încărcare este de 450mA deci este necesară utilizarea unui acumulator de capacitate minimă 450mAh. În testul nostru vom utiliza o celulă solară de 2.5W / 9V și un acumulator LiPo de 800mAh.  Pentru mai multe detalii despre funcționarea componentei MPTT puteți consulta materialul „Sunny Buddy Solar Charger V13 Hookup Guide”.

Pentru partea de achiziție și raportare IoT vom utiliza placa de dezvoltare Adafruit Feather 32u4 FONA echipată cu un microcontroler ATmega32U4 (la fel ca și placa de dezvoltare Arduino Leonardo) și un controler GSM/2G SIM800 quad-band (850/900/1800/1900MHz).

2

Placa va supraveghea nivelul de încărcare a acumulatorului și va raporta prin Internet (prin intermediul conexiunii GPRS) valorile către serviciul cloud iot.robofun.ro. Placa de dezvoltare se va alimenta de la acumulatorul LiPo conectat la componenta MPPT și va servi ca element de descărcare pentru acesta. Pentru mai multe detalii despre funcționarea și utilizarea plăcii de dezvoltare Adafruit Feather 32U4 FONA puteți consulta materialul „Overview | Adafruit Feather 32u4 FONA | Adafruit Learning System”.

Diagrama de interconectare între componentele sistemului de test este următoarea:

4

Funcționarea plăcii de dezvoltare necesită o antenă GSM uFL. Pentru a minimiza consumul și pentru a putea controla inițializarea sistemului (mai ales în cazul în care acumulatorul sistemului nu este încărcat suficient) pinul KEY al modulului GSM se va deconecta de la masă (necesită tăierea jumperului KEY de pe spatele plăcii) și se va conecta la pinul 0 al plăcii de dezvoltare (firul galben din schema de interconectare).  În acest fel modulul GSM de pe placă nu va mai fi activ continuu ci doar în cazul în care îl vom activa din software. ATENȚIE!!! Această operație conduce la pierderea garanției oferite de producător.

Programul sistemului a fost dezvoltat și testat utilizând Arduino IDE 1.8.5 având instalate extensia Adafruit AVR Boards 1.4.11 și bibliotecile Adafruit FONA Library 1.3.3 și Sleep_n0m1 1.1.1.

#include “Adafruit_FONA.h”

#include <Wire.h>

#define FONA_RX 9

#define FONA_TX 8

#define FONA_RST 4

#define FONA_RI 7

#define FONA_KEY 1

#define FONA_ALARM 13

Definirea constantelor apn, apnusername și apnpassword sunt specifice rețelei de telefonie mobile al SIM-ului GSM utilizat. ATENȚIE!!! Placa de dezvoltare necesită un SIM GSM ce suportă standardul 2G, comunicația GSM nu va funcționa în cazul unui SIM GSM exclusiv 3G/4G.

#define apn “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);

#include <Sleep_n0m1.h>

Sleep sleep;

În cadrul secțiunii setup() se vor inițializa pinii de control utilizați pentru controlul modulului GSM (FONA_KEY este utilizat pentru activarea modulului GSM, FONA_ALARM este conectat la LED-ul de semnalizare al plăcii de dezvoltare și va fi utilizat pentru avertizarea imposibilității inițializării corecte a modulului GSM).

void setup() {

  pinMode(FONA_KEY,OUTPUT);

  digitalWrite(FONA_KEY,HIGH);

  pinMode(FONA_ALARM,OUTPUT);

  digitalWrite(FONA_ALARM,LOW);

}

Secțiunea loop() conține algortimul complet de inițializare și comandă pentru modulul GSM, achiziția nivelului de încărcare a bateriei și transmiterea prin Internet a valorilor către serviciul IoT. Primul pas în execuția algoritmului este intrarea în starea de consum redus pentru o oră – stare obligatorie pentru revenirea sistemului dintr-o stare de oprire completă generată de consumarea completă a acumulatorului (acumulatorul este încărcat suficient pentru inițializarea microcontrolerului ATmega32U4 dar nu și pentru inițializarea modulului GSM).

void loop() {

  sleep.pwrDownMode();

  sleep.sleepDelay(3600000);

Pornirea modulului GSM necesită un impuls de 2 secunde pe pinul FONA_KEY. Sistemul va încerca de 10 ori inițializarea modulului GSM după care va relua perioada de o oră de consum redus.

  digitalWrite(FONA_KEY,LOW);

  digitalWrite(FONA_ALARM,HIGH);

  delay(2000);

  digitalWrite(FONA_KEY,HIGH);

  digitalWrite(FONA_ALARM,LOW);

  delay(2000);

  fonaSerial->begin(4800);

  byte i = 0;

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

      i++;

      if (i==10) break;

      digitalWrite(FONA_KEY,LOW);

      digitalWrite(FONA_ALARM,HIGH);

      delay(2000);

      digitalWrite(FONA_KEY,HIGH);

      digitalWrite(FONA_ALARM,LOW);

      delay(2000);      

  }

În cazul în care modulul GSM s-a inițializat corect se va încerca deblocarea cartelei SIM pe baza codului PIN (trebuie modificat în program în funcție de codul cartelei folosite). Dacă deblocarea cartelei SIM nu se poate face se va relua perioada de o oră de consum redus – este posibil ca acumulatorul să scadă sub pragul de alimentare corectă a modulului GSM între inițializare și deblocarea cartelei SIM.

if (i!=10) {

    if (! fona.unlockSIM(“0000”) ) {

digitalWrite(FONA_ALARM,HIGH);

delay(1000); i=10; }

    else {  

Programul va aștepta înregistrarea în rețeaua GSM a cartelei SIM a sistemului pentru o perioadă de 100 de secunde.

    uint8_t n = fona.getNetworkStatus();

     i=0;

      while (n!=1) {

        i++;

        if (i==10) break;

        digitalWrite(FONA_ALARM,HIGH);

        delay(5000);

        digitalWrite(FONA_ALARM,LOW);

        delay(5000);

        n = fona.getNetworkStatus();

       }

     }

Postarea valorii de încărcare a acumulatorului se va face doar dacă se va inițializa cu succes partea de comunicații de date (fona.enableGPRS). Măsurarea se va face chiar de modulul GSM. În cadrul programului trebuie personalizate cheile de autentificare (SENSOR_TOKEN1 și SENSOR_TOKEN2) obținute prin înregistrarea gratuită în cadrul serviciului iot.robofun.ro.

if(i!=10) {

      fona.setGPRSNetworkSettings(F(apn),

F(apnusername),F(apnpassword));

      if (fona.enableGPRS(true)) {

      uint16_t vbat;

      uint16_t pbat;

      fona.getBattVoltage(&vbat);

      fona.getBattPercent(&pbat);

      delay(1000);

      uint16_t statuscode;

      int16_t length;

      String SENSOR_TOKEN1=”…”;

      String SENSOR_TOKEN2=”…”;

      String data =

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

      char url[100];

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

      fona.HTTP_GET_start(url, &statuscode,

(uint16_t *)&length);

      while (length > 0) {

          while (fona.available()) {

            char c = fona.read();

            length–;

            if (! length) break;

          }

          break;

      } 

      fona.HTTP_GET_end();

      delay(100);

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

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

      fona.HTTP_GET_start(url, &statuscode,

(uint16_t *)&length);

      while (length > 0) {

          while (fona.available()) {

            char c = fona.read();

            length–;

            if (! length) break;

          }

          break;

      } 

      fona.HTTP_GET_end();

      delay(100);

    }

La final, înainte de reintrarea în modul de consum pentru o oră, se va opri modulul GSM printr-un puls de 2 secunde pe pinul FONA_KEY.

      digitalWrite(FONA_KEY,LOW);

      delay(2000);

      digitalWrite(FONA_KEY,HIGH);

    }

  }

}

Testul a fost efectuat pornind cu acumulatorul sistemului complet consumat (tensiune sub 3.3V). După o zi cu soare, când tensiunea acumulatorului a ajuns la 3.7V, sistemul s-a inițializat complet cu succes și a început să posteze pe serviciul IoT. Acest lucru înseamnă că sistemul își poate reveni dintr-o stare de oprire completă generată de descărcarea acumulatorului (mai multe zile fără lumină solară) fără intervenție umană.

5

În timpul unei zile cu soare acumulatorul câștigă aproximativ 30% din nivelul de încărcare iar procentul de descărcare într-o zi înorată este de circa 15%. Asta înseamnă că o zi cu soare poate fi urmată de două zile înorate fără ca sistemul să descarce acumulatorul. Dacă acumulatorul sistemului este complet încărcat, sistemul poate funcționa fără lumină solară pentru un interval de 6-7 zile

6

Testul efectuat dovește că se poate construi un sistem de raportare IoT GSM/GPRS care să funcționeze în mod autonom pe baza energiei solare fără să fie nevoie de intervenția umană în cazuri speciale de descărcare a acumulatorului.

Solar Power WiFi Test

Implementarea unui proiect cu alimentare solară prezintă mai multe avantaje printre care, bineînțeles, se evidențiază independența de rețeaua electrică tradițională. În același timp un astfel de proiect ridică și multe probleme legate de dimensionarea soluției de alimentare și alegerea componentelor potrivite. Care este dimensiunea panoului solar? Ce soluție de stocare a energiei electrice alegem? Ce soluție de încărcare a acumulatorilor este cea mai potrivită? Acestea sunt câteva dintre întrebările la care vom încerca să răspundem în cadrul acestui proiect. Chiar dacă se poate face o evaluare inițială la nivel teoretic, în cadrul testului nostru vom alege varianta testării efective a soluției alese și evidențierea avantajelor și a punctelor slabe ale acesteia.

Ca soluție de maximizare a eficienței captării energiei solare și de încărcare a acumulatorului vom utiliza o componentă MPPT: Sunny Buddy – MPPT Solar Charger.

2

Această componentă permite conectarea unei celule solare cu ieșirea între 6V și 20V și încărcarea unui acumulator LiIon sau LiPo cu o singură celulă (3.7V). Curentul maxim de încărcare este de 450mA deci este necesară utilizarea unui acumulator de capacitate minimă 450mAh. În testul nostru vom utiliza o celulă solară de 2.5W / 9V și un acumulator LiPo de 800mAh. Pentru a supraveghea procesul de încărcare / descărcare a acumulatorului vom utiliza o placă de dezvoltare Adafruit Feather HUZZAH ce va raporta datele măsurate prin intermediul conexiunii WiFi către serviciul cloud iot.robofun.ro. Placa de dezvoltare se va alimenta de la acumulatorul LiPo conectat la componenta MPPT și va servi ca element de descărcare pentru acesta. Pentru o acuratețe mai bună a măsurătorilor vom utiliza o componentă ce va monitoriza nivelul de încărcare a bateriei: LiPo Fuel Gauge. Componenta de monitorizare se va intercala între ieșirea componentei MPPT și alimentarea plăcii de dezvoltare și va raporta prin intermediul unei magistrale I2C nivelul de încărcare a bateriei.

3

Diagrama de interconectare între componentele sistemului de test este următoarea:

4

Înainte de realizarea montajului și punerea lui în funcțiune este recomandată parcurgerea documentațiilor oficiale ale componentelor utilizate:

Atenție!!! Schema precedentă este schema finală de test. În momentul programării plăcii de dezvoltare trebuie să deconectăm alimentarea plăcii prin intermediul componentei de măsurare (să deconectăm placa de dezvoltare de componenta LiPo Fuel Gauge) și să deconectăm pinul Reset de pinul Wake (GPIO16) – firul galben.

Programul sistemului a fost dezvoltat și testat utilizând Arduino IDE 1.8.5, extensia ESP8266 Community 2.4.0 și biblioteca LiFuelGauge 1.0.

#include <Wire.h>

#include <ESP8266WiFi.h>

#include <ESP8266HTTPClient.h>

#include <LiFuelGauge.h>

În cadrul programului trebuie personalizate datele de conectare la rețeaua WiFi (variabilele ssid[] și pass[]).

char ssid[] = “…”;

char pass[] = “…”;

LiFuelGauge gauge(MAX17043);

Secțiunea setup() se va ocupa cu inițializarea comunicației cu modulul de măsurare a tensiunii din acumulator și cu inițializarea comunicației WiFi.

void setup() {

  gauge.reset();

  WiFi.mode(WIFI_STA);

  WiFi.hostname(“ESP_SolarMeter”);

  WiFi.begin(ssid, pass);

  byte retry = 0;

  while ((WiFi.status() != WL_CONNECTED) && (retry<10)) {

    delay(1000);

    retry++;

    if (retry==10) ESP.restart();

  }

}

Secțiunea loop() conține partea de citire a măsurătorilor efectuate de componenta LiPo Fuel Gauge și de trimiterea acestor măsurători către serviciul IoT. În cadrul programului trebuie personalizate cheile de autentificare (SENZOR_TOKEN1 și SENZOR_TOKEN2) obținute în urma înregistrării gratuite pe iot.robofun.ro. Postarea măsurătorilor se va face la un interval de o oră. Între măsurători sistemul va funcționa în mod de consum redus (deepSleep) pentru a minimiza consumul de energie și maximiza durata de funcționare.

void loop() {

  float batPercentage = gauge.getSOC();

  float batVoltage = gauge.getVoltage(); 

  String SENSOR_TOKEN1=”…”;

  String SENSOR_TOKEN2=”…”;

  HTTPClient http;

  String data =

String(“http://iot.robofun.ro/api/v1/senzor/&#8221;) + SENSOR_TOKEN1 + “/input?value=” + String(batVoltage, DEC);

  http.begin(data);

  int httpCode = http.GET();

  delay(100);

  http.end();

  data = String(“http://iot.robofun.ro/api/v1/senzor/&#8221;) + SENSOR_TOKEN2 + “/input?value=”  + String(batPercentage, DEC);

  http.begin(data);

  httpCode = http.GET();

  delay(100);

  http.end();

  ESP.deepSleep(3600L*1000000L);

}

Testarea sistemului va indica următoarele lucruri esențiale în funcționarea acestuia:

  • Sistemul de încărcare solar este capabil să înmagazineze într-o zi însorită suficientă energie pentru funcționarea sistemului pentru 24 de ore – sistemul poate funcționa autonom într-o perioadă însorită. Mai jos este graficul furnizat de serviciul iot.robofun pentru o succesiune de două zile însorite consecutive. Se poate observa că sistemul recuperează energia consumată peste noapte în timpul zilei.

5

6

  • Peste noapte sistemul pierde circa 30% din capacitatea acumulatorului. Asta înseamnă că sistemul poate funcționa circa trei zile fără reîncărcare (zile fără soare / înorate).

7

  • După epuizarea completă a energiei din acumulator sistemul nu este capabil să repornească singur chiar dacă urmează o zi însorită. Acest lucru este cauzat de curentul mare necesar la inițializarea plăcii de dezvoltare (peste 450mA, peste curentul furnizat de componenta Sunny Buddy). Sistemul nu va reuși să intre în mod de consum redus pentru a lăsa acumulatorul să se reîncarce și va rămâne într-o secvență infinită de inițializare ce va opri procesul de reacumulare a energiei solare.

Testul relevă posibilitatea limitată de funcționare a sistemului propus. Acesta poate funcționa o perioadă nedeterminată de timp în mod autonom atâta timp cât nu apare o succesiune de mai mult de două zile complet înnorate. Curentul mare necesar la inițializarea plăcii de dezvoltare blochează reluarea procesului de reîncărcare solară. Totuși, cu un alt consumator ce necesită un curent mai mic de inițializare, sistemul ar putea funcționa autonom și și-ar putea relua activitatea după o perioadă lungă fără lumină solară.

Optimizarea programelor în mediul Arduino IDE

Optimizarea procesului de compilare

Mediul Arduino IDE utilizează compilatorul GCC. Pe lângă procesul de compilare propriu-zis, mediul de dezvoltare efectuează o serie de operații (pre-procesare) ce permit implementarea funcțiilor specifice Arduino (de exemplu includerea automată a definițiilor din Arduino.h). Pentru detalierea procesului de compilare se poate parcurge materialul „Arduino Build Process”.

Procesul de compilare este efectuat, invizibil pentru programator, la un anumit nivel de optimizare (-Os sau -O2 în funcție de versiunea mediului de dezvoltare). Pentru detalierea nivelurilor de optimizare recomandăm parcurgerea materialului „Using the GNU Compiler Collection (GCC): Optimize Options”. Modificarea nivelului de optimizare la compilarea programului se poate face la nivel de fișiere de configurare a mediului de dezvoltare sau chiar la nivel de program.

Prin introducerea unor directive de compilare putem modica nivelul de optimizare pentru întregul program:

#pragma GCC optimize (“-O0”)

#pragma GCC push_options

void setup() { }

void loop() { }

#pragma GCC pop_options

sau doar pentru o anumită secțiune, funcție sau procedură:

void loop() __attribute__((optimize(“-O0”)));

void loop() { }

 

Optimizarea instrucțiunilor specifice mediului Arduino IDE

Unul din obiectivele principale ale mediului Arduino IDE este înlesnirea accesului programatorului la diversele mecanisme interne ale microcontrolerului. Din aceste motiv s-a efectuat rebotezarea pinilor și tot din același motiv au fost introduse instrucțiuni specifice mediului Arduino IDE de tipul pinMode, digitalRead sau digitalWrite. Din punct de vedere al procesului de învățare și dezvoltare aceste facilități accelerează foarte mult timpul de lucru dar din punctul de vedere al eficienței execuției aduc penalizări destul de mari. Cel mai bun exemplu, prezentat și în materialul „Arduino Is Slow”, este înlocuirea instrucțiunii digitalWrite cu o instrucțiune de modificare a registrului intern ce stochează starea pinului pe care dorim să-l modificăm. Se poate testa diferența dintre timpul de execuție a celor două variante rulând următorul exemplu de program:

void setup()

{

 Serial.begin(9600);

}

void loop()

{

 int initial = 0;

 int final = 0;

 initial = micros();

 for(int i = 0; i < 500; i++)

 {

    digitalWrite(13,HIGH);

    digitalWrite(13,LOW);

 }

 final = micros();

 Serial.print(“Time for digitalWrite(): “);

 Serial.print(final-initial);

 Serial.println(“”);

 initial = micros();

 for(int i = 0; i < 500; i++)

 {

    PORTB |= _BV(PB5);

    PORTB &= ~_BV(PB5);

 

 }

 final = micros();

 Serial.print(“Time for true c command: “);

 Serial.print(final-initial);

 while(1);

}

Programul înlocuiește instrucțiunea digitalWrite cu o scriere în registrul intern al microcontrolerului (pinul 13 este pinul PB5). Rezultatul este destul de convingător:

2

Instrucțiunea digitalWrite nu este singura care poate fi înlocuită în cadrul programelor Arduino pentru a obține performanțe mai bune. O altă instrucțiune ”delicată” ce poate afecta funcționarea unor montaje mai complicate este instrucțiunea shiftOut.  Dacă examinăm fișierul wiring_shift.c din directorul Arduino putem vedea că această funcție se bazează pe instrucțiunea digitalWrite deci poate fi accelerată prin înlocuirea acestei instrucțiuni.

void shiftOut(uint8_t dataPin, uint8_t clockPin,

uint8_t bitOrder, uint8_t val)

{

    uint8_t i;

    for (i = 0; i < 8; i++)  {

         if (bitOrder == LSBFIRST)

             digitalWrite(dataPin, !!(val & (1 << i)));

         else

             digitalWrite(dataPin, !!(val & (1 <<

(7 – i))));

         digitalWrite(clockPin, HIGH);

         digitalWrite(clockPin, LOW);     

    }

}

ATENȚIE!!! Optimizările prezentate conduc la pierderea portabilității programelor între diverse plăci Arduino. Toate indicațiile se aplică direct doar pentru plăcile ce sunt echipate cu un microcontroler ATmega328P (Arduino Uno de exemplu).

Pentru exemplificarea optimizării instrucțiunii shiftOut vom considera următorul montaj bazat pe o placă de dezvoltare Arduino Uno, un registru de deplasare 74HC595 și 8 leduri:

3

Programul propus va implementa un mini joc de lumini ce constă în aprinderea succesivă a câte un led din cele 8. Programul obișnuit (ce utilizează instrucțiunea shiftOut) este:

const int latchPin = 4;

const int clockPin = 3;

const int dataPin = 2;

void setup() {               

  pinMode(latchPin, OUTPUT);

  pinMode(dataPin, OUTPUT); 

  pinMode(clockPin, OUTPUT);

  digitalWrite(latchPin, LOW);

  shiftOut(dataPin, clockPin, MSBFIRST, 0); 

  digitalWrite(latchPin, HIGH);

}

void loop() {

  int afisare = 1;

  while (1) {

   for (int i=0;i<8;i++) {

      digitalWrite(latchPin, LOW);

      shiftOut(dataPin, clockPin, MSBFIRST, afisare << i); 

      digitalWrite(latchPin, HIGH);

      delay(500);

     }

   }

}

Varianta nouă ce nu folosește instrucțiunile shiftOut și digitalWrite este:

void loop() {

  int afisare = 1;

  while (1) {

   for (int i=0;i<8;i++) {

    PORTD |= _BV(PD4);

    for (uint8_t b = 0; b < 8; b++)  {

      if(!!((afisare << i) & (1 << (7 – b)))) PORTD |=

_BV(PD2);

      else PORTD &= ~_BV(PD2);

      PORTD |= _BV(PD3);

      PORTD &= ~_BV(PD3);

    }

    PORTD &= ~_BV(PD4);

    delay(500);

   }

  }

}

Chiar dacă la prima vedere exemplul funcționează la fel, se poate justifica necesitatea modificării codului prin imaginarea situației în care procesul de transmitere serială se repetă de un număr suficient de mare de ori încât produce o întârziere inacceptabilă (de exemplu încascadarea unui număr mare de registre de deplasare).