Циљ вежбе


Научити се да израђујемо уређаје, који су у складу са концепцијом IoT Alterozooma


Задаци вежбе

1. Установа библиотека за протокол узајамног деловања са Аlterozoomom
2. Писање скеча
3. Провера интеракције са уређајем уз помоћ монитора серијског порта


Инструменти за извршавање вежбе

1. Умрежени рачунар
2. Платформа Arduino са USB излазом (на пример Arduino UNO)


Теоријски део

За упрошћавање израде уређаја  IoT и сједињавање његовог прикључака са платформом Alterozoom био је израђен прост, текстуални протокол за узајамно деловање уређаја са рачунаром кроз различите канале везе, који су одређени за паралелну размену информација између њих. Протокол је предодрећен за организацију узајамног деловања два система (тачка - тачка) посредством размене простих текстуалних порука. Коришћење текстуалних порука упрошћава израду и чување уређаја тако што је дозвољено узајамно деловање са њим без специјалног ПО из  комадне линије или монитора серијског порта. Опис протокола се налази на линку: http://wl.unn.ru/ftp/public/IoT/Alterozoom/Alterozoom IoT protocol ptp.pdf
Такође је била израђена и библиотека за Arduino IDE која реализује дати протокол.


Извршавање вежбе

1. Установа библиотека за протокол узајамног деловања са Аlterozoomom

Преузимамо архив са библиотекама за Arduino IDE са линка: http://wl.unn.ru/ftp/public/IoT/Alterozoom/ARpc.zip. Инсталирамо библиотеку помоћу менаџера библиотека: бирамо ставку менија "Скетч => Управление библиотеками => Добавить .ZIP библиотеку" и налазимо преузети архив ARpc.zip.


2. Писање скеча

У нашој вежби написаћемо скеч који ће дозвољавати да из интерфејса Alterozooma палимо и гасимо диоду и који ће приказивати мерења сваких пола секунде(генерисани дводимензиони сигнал (sin(t);cos(t)) ). Такође ће у систему бити још један сензор, бројач трептања диоде.
За узајамно деловање са рачунаром путем серијског порта ћемо направити објекат класа ARpc и определити за њега две callback - функције, једну за израду команди од рачунара и другу за слање обавештења на рачунар. Дане функције ће позивати сама библиотека кад јој то буде неопходно. Због једнозначне идентификације система такође је непходно указати идентификатор и име система. А да би обезбедили могућност управљања системом неопходно је направити xml - опис панела за управљање системом.
Креирамо нови скеч и чувамо га под називом AlterozoomTest. Проверавамо да ли је изабрана одговарајућа платформа и порт. Нашем скечу прикључујемо библиотеку ARpc (Скетч => Подключить библиотеку => ARpc). На почетку фајла треба да се појави: #include <ARpc.h>. 
Генеришемо уникални идентификатор у формату UUID (на пример, можемо се послужити сервисом: https://www.uuidgenerator.net/version4, при отварању странице на врху ће бити готов UUID). Додајемо две глобалне промењиве за индендификатора и име уређаја:

const char *deviceName="led_blink_test";//имя уређаја
const ARpcUuid deviceId("{xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx}");//идентификатор уређаја

xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx - мењамо на UUID који смо добили и не бришемо витичасте заграде. Такође опредељуљемо додатне промењиве:

int ledPin=13;//пин диоде
unsigned long blinksCount=0;//број трептаја 

Да би у будућем интерфејс за наш уређај био доступан морамо креирати xml - опис. Више о томе можете прочитати у PDF документу (линк који се налази у теоријском делу). Нашој вежби је сада потребно и једно дугме које ће слати сигнал "blink" контролеру и њега ћемо описати на следећи начин:

const char *interfaceStr="<controls><group title=\"Device controls\"><control sync=\"0\" title=\"Blink\" command=\"blink\"/></group></controls>";

 Такође да би добили податке од нашег уређаја неопходно је описати сензор. У нашем случају то изгледа овако:

const char *sensorsDef="<sensors>"
"<sensor name=\"blinks_count\" type=\"single\"/>"//датчик blinks_count
"<sensor name=\"sin_x\" type=\"single\"><constraints dims=\"2\"/></sensor>"//датчик sin_x (двомерни)
"</sensors>";

Опредељујемо класу за предају команди на PC кроз серијски порт. 

class WriteCallback
    :public ARpcIWriteCallback
{
public:
    virtual void writeData(const char *data,unsigned long sz)
    {
        Serial.write(data,sz);
    }
    virtual void writeStr(const char *str)
    {
        Serial.print(str);
    }
}wcb;

Објекат те класе се користи када је библиотеци потребно предати неке податке са уређаја. У даном случају то су подаци које ћемо ми видети у монитору порта. Обратите пажњу да се не користи println. То је зато што та функција додаје непотребан превод реда који би реметио нормалну обраду података.
Даље објацљујемо објекат класе ARpc и предајемо му адресе за креирање више промењивих и функције:

ARpcDevice parser(300,&wcb,&deviceId,deviceName);

Опредељуљемо класу која обрађује команди које се предају уређају. Библиотека ће користити објекат те класе, када на уређај буду пристизале команде које смо одредили и унели ми у монитору порта. Функција обраде прихвата команде, аргументе команде, број аргумената:

class CommandCallback
    :public ARpcIDevEventsCallback
{
public:
    virtual void processCommand(const char *cmd,const char *args[],unsigned char argsCount)
    {
        if(strcmp(cmd,"blink")==0)//команда blink, проверавамо да ли је аргумент
        {
            digitalWrite(13,HIGH);
            delay(500);
            digitalWrite(13,LOW);
            ++blinksCount;
            parser.disp().writeMeasurement("blinks_count",String(blinksCount).c_str());
            parser.disp().writeOk();
        }
        else parser.disp().writeErr("Unknown cmd");//непозната команда
    }
}ccb;

Овде ми обрађујемо команду - "blink", која када дође, проузрокује трептај диоде на порту 13, и предаје нову вредност "мерења" бројача трептаја.
Даље пишемо функцију за генерисање очитавања синуса и косинуса

int t=0;
void writeSinVal()
{
    const char *strs[2];
    String sinVal(sin(0.1*t)),cosVal(cos(0.1*t));
    parser.disp().writeMeasurement("sin_x",sinVal.c_str(),cosVal.c_str());
    ++t;
}

300 је величина буфера за једно обавештење. Немогуће је предати контролеру обавештење које је веће од наведеног. 
Величина буфера се одређује и од зависности обима нама доступне памети. И према томе на микроконтролерима који имају велику памет можемо користити и веће буфере. 
Проводимо иницирање пина и серијског порта у функцији setup() и одређујемо опис сензора и интерфејса управљања

void setup()
{
    Serial.begin(9600);
    pinMode(ledPin,OUTPUT);
    parser.disp().installDevEventsHandler(&ccb);
    parser.disp().setControls(interfaceStr);
    parser.disp().setSensors(sensorsDef);
}

И за крај у функцији loop() је неопходно проверавати серијски порт и да ли су доступни нови подаци, предавати њих објекту parser, генерисати нову вредност синуса и после тога направити паузу од пола секунде да се мерења не би генерисала превише брзо.

void loop()
{
    while(Serial.available())
        parser.putByte(Serial.read());
    writeSinVal();
    delay(500);
}

 Учитавамо добијени скеч на микроконтролер.
 

3. Провера интеракције са уређајем уз помоћ монитора серијског порта


Отварамо монитор порта. У њему су дужна регуларно да се појављују обавештења са значењем "meas" и са новим вредностима синуса и косинуса.

Проверите да ли је доле одабрано "Новая строка"(нови ред), а не "Нет конца строки"(нема краjа реда). У пољу пишемо "identify" и притискамо дугме "Отправить"(послати). Као одговор је дужно да се појави обавештење deviceinfo. 

У том обавештењу треба да се налазе подаци о идентификатору и имену уређаја које смо указали у скечу.

Додатни задатак: Предати обавештење у уређај са командом трептања диоде "blink", затражити од уређаја списак сензора и опис интерфејса управљања.