Pojďme programovat elektroniku: Postavíme si bezdotykové ovládání PC za stokorunu

  • Vzpomínáte na Leap Motion?
  • Dnes si postavíme něco podobného za zlomek ceny
  • Pohrajeme si s optickým detektorem gest

Před čtyřmi lety začali první nadšenci ve svých schránkách objevovat velké obálky a v nich krabičku Leap Motion od stejnojmenného kalifornského výrobce, který sliboval revoluci v ovládání počítače.

Namísto myši a klávesnice totiž vytvořil senzor pro bezdotykové ovládání prostým pohybem ruky. Podobná technologie tou dobou sice nebyla žádnou novinkou – vždyť takový Kinect byl tehdy již roky k dispozici nejen pro Xbox, ovšem Leap poblouznil komunitu ohromnou přesností, kdy správně zaregistroval a rozlišil pohyb jediného prstu.

Leap Motion nicméně předběhl dobu a brzy se ukázalo, že technologie sice funguje, ale chybí pro ni aplikace a dost možná i potřeba. Bylo to hezké, bylo to přesné, bylo to efektní, ale… Ale až na early adoptery to nikdo nepotřeboval a bublina brzy splaskla. Leap Motion během roku prodal necelý půl milionu krabiček, což bylo hluboko pod původním očekáváním, a začal propouštět inženýry.

Firma dodnes existuje a nadále svoji technologii rozvíjí, namísto náhrady myši a klávesnice se ale s partnery vrhla spíše do vývoje ovladačů pro virtuální realitu.

Takový malý Leap Motion za stovku

Původní Leap Motion první generace jste si mohli pořídit za cirka 90 amerických dolarů včetně poštovného. My si dnes v rámci našeho seriálu Pojďme programovat elektroniku vyzkoušíme něco podobného a bude nás to stát okolo stovky. A to nepíšu o dolarech, ale samozřejmě o korunách.

Vedle armády všemožných meteorologických a dalších elektronických senzorů za pár korun totiž na e-shopech narazíte i na optický senzor APDS-9960. Takový Sparkfun jej prodává za necelých 15 dolarů, čínskou verzi z eBaye a jemu podobných ale seženete opravdu za částku okolo stokoruny i méně.

Klepněte pro větší obrázek Klepněte pro větší obrázek Klepněte pro větší obrázek
Drobný optický senzor je jen o něco větší než karta microSD. K mikropočítači jsem ji připojil skrze rozhraní I2C a 3,3V napájení. Žlutý vodič propojuje piny přerušení. O něm více později.

Drobná destička s rozhraním I2C toho zvládne docela hodně počínaje senzorem jasu (ambientního světla) a konče detektorem barvy (RGB), my si však dnes vyzkoušíme detekci gest rukou, kterou budeme pohybovat sem a tam pár centimetrů před senzorem.

Budeme k tomu potřebovat knihovnu pro Arduino IDE, kterou stáhnete z GitHubu, samozřejmě nějaký mikrokontroler (já použiji levný klon základního modelu Arduino Uno) a to je všechno. Scénář bude jednoduchý. Arduino bude pomocí senzoru detekovat čtyři základní pohyby: vlevo, vpravo, nahoru a dolu, a pokud takový pohyb zaregistruje, pošle skrze USB kabel do sériové linky textový znak, který identifikuje daný pohyb.

Klepněte pro větší obrázek Klepněte pro větší obrázek Klepněte pro větší obrázek
Od prostého výpisu směru pohybu ruky v sériové lince po program napsaný v C#, který pohyb převede na stisk směrové klávesy

Na PC pak poběží program, který bude poslouchat komunikaci na sériové lince, a pokud dorazí onen textový znak, simuluje stisk některé ze směrových kláves. Pokud bych tedy měl spuštěný třeba prohlížeč a v něm webové mapy, kterými mohu posouvat šipkami na klávesnici (třeba Google Maps), krátce po mávnutí rukou doleva se oním směrem o něco posunou i mapy v prohlížeči. Heuréka!

Výpis údajů do sériové linky

Výpis v programu na PC

A konečně převod pohybu ryky na stisky směrových kláves

Interrupt, čili přerušení

Tak a teď se opět podíváme na zdrojový kód samotného senzoru. Jelikož používám hotovou knihovnu, je vlastně docela primitivní, protože veškerou špinavou práci provádí křemíkový obvod a ona knihovna, nicméně jedna novinka by tu oproti našim dosavadním projektům přeci jen byla: interrupt, tedy přerušení.

Přerušení je mocný nástroj, který umožní při určité události asynchronně zpracovat instrukce programu, který zrovna dělá něco jiného. Program bude vlastně vyrušen, z čehož vyplývá i pojmenování této technologie.

No dobrá, vysvětlím to lépe. Častou událostí může být třeba vyšší napětí na některém z pinu Arduina třeba po stisknutí tlačítka. Tuto událost mohu zachytit klasickým synchronním způsobem třeba ve smyčce loop, ve které budu stále dokola kontrolovat, jestli se třeba na pinu 3, na kterém je připojené tlačítko, neobjevilo vyšší napětí a tedy logická jednička signalizující stisk:

void loop() {
  if ( digitalRead(3) == HIGH ) {
    Serial.println("Stisk tlacitka!");
  }
}

Co kdybych ale nechtěl tímto způsobem stále dokola kontrolovat napětí na pinu ve smyčce loop, ale řekl bych Arduinu, ať samo zpracuje náš příkaz ve chvíli, kdy dojde ke změně napětí na pinu a nehledě na to, co zrovna program dělá?

Přesně s tím nám pomůže ono přerušení, které ovšem zpravidla podporují jen některé piny řídícího čipu. V případě Arduin najdete jejich seznam třeba na této stránce.

Arduino Uno podporuje přerušení na digitálních pinech 2 a 3. Kdybych tedy pro tlačítko použil pin 3, kód by mohl vypadat takto:

void setup() {
  pinMode(3, INPUT);
  /*
    Registrace preruseni na pinu 3. Po preruseni se zavola
    stejnojmenna funkce preruseni. Posledni parametr urcuje
    typ preruseni. RISING znamena, ze na pinu 3 se nahle
    zvysilo napeti, a proto se vyvola preruseni. Dale bych
    mohl pouzit jeste FALLING, CHANGE (jakakoliv zmena) a LOW.
  */
  attachInterrupt(digitalPinToInterrupt(3), preruseni, RISING);
}

void preruseni() {
   // Stiskl jsem tlacitko
}

Funkce přerušení se používá v celé hromadě scénářů – velmi často třeba pro probuzení mikrokontroleru z úsporného režimu. V takovém stavu může být drtivá většina logiky čipu pozastavená, funkce přerušení ale běží dál, takže když na tomto pinu dojde ke změně napětí, mikrokontroler se probudí. Takto jej může probudit třeba externí modul RTC hodin, který ve stanovený čas vyšle signál na předem zvolený pin přerušení, a probudí se tak celé Arduino.

Teď už konečně ten kód

Po stručném výkladu, jak v principu funguje přerušení, zpět k naší levné parodii na Leap Motion. Optický čip APDS-9960 má vedle I2C rozhraní ještě pin INT (interrupt), který používá pro vyvolání přerušení v okamžiku, kdy detekuje (třeba) pohyb.

To znamená, že pin INT propojíme s pinem přerušení na mikropočítači, a když APDS-9960 zjistí pohyb, sníží na pinu INT napětí (přerušení typu FALLING, viz seznam režimů), což zaregistruje naše Arduino a bude moci zpracovat další kód.

Jelikož by se měla samotná funkce vyvolaná přerušením zpracovat co nejrychleji, aby zbytečně neblokovala tok programu, často se jen změní hodnota proměnné a na tu již zareaguje podmínka ve stále dokola se opakující smyčce loop. Stejně tak je tomu i v našem programu.

// Knihovna pro praci s rozhranim I2C
#include <Wire.h>
// Knihovna pro praci se senzorem APDS9960
#include <SparkFun_APDS9960.h>

// Pin pro signal preruseni
#define pin 3

// Objekt naseho senzoru pohybu a pomocna stavova promenna
SparkFun_APDS9960 detektor = SparkFun_APDS9960();
volatile int akce = 0;

// Funkce setup se zpracuje jen jednou po spusteni mikropocitace
void setup() {
  // Nastaveni pinu pro preruseni na vstup
  pinMode(pin, INPUT);
  // Nastartovani seriove linky
  Serial.begin(115200);
  // Registrace preruseni (FALLING: pokud napeti klesne, vyvolej preruseni)
  attachInterrupt(digitalPinToInterrupt(pin), preruseni, FALLING);
  // Spusteni detektoru gest
  detektor.init();
  detektor.enableGestureSensor(true);
}

// Funkci program zavola pri preruseni a nastavi promennou akce na 1
void preruseni() {
  akce = 1;
}

// Funkce loop se opakuje stale dokola
void loop() {
  // Pokud nastal nejaky pohyb/gesto
  if ( akce == 1 ) {
    // Nejprve pozastavim preruseni
    detachInterrupt(digitalPinToInterrupt(pin));
    // Zavolam funkci, ktera mi rekne, co to bylo za gesto
    zpracovatGesto();
    // Vynuluji pomocnou promennou a opet nastartuji preruseni
    akce = 0;
    attachInterrupt(digitalPinToInterrupt(pin), preruseni, FALLING);
  }
}

// Funkce pro zpracovani gesta
void zpracovatGesto() {
  /* Pokud senzor detekoval pohyb vlevo, vpravo,
      dolu, nahoru aj., posli do seriove linky znak
      U, D, L, R, +, -, nebo M pro neznamy pohyb
  */
  if ( detektor.isGestureAvailable() ) {
    switch ( detektor.readGesture() ) {
      case DIR_UP:
        Serial.println("U");
        break;
      case DIR_DOWN:
        Serial.println("D");
        break;
      case DIR_LEFT:
        Serial.println("L");
        break;
      case DIR_RIGHT:
        Serial.println("R");
        break;
      case DIR_NEAR:
        Serial.println("+");
        break;
      case DIR_FAR:
        Serial.println("-");
        break;
      default:
        Serial.println("M");
    }
  }
}

Program na straně PC je už samozřejmě mnohem komplexnější a jeho popis je nad rámec tohoto článku, projekt pro Visual Studio Community 2017 primitivního programu napsaného v C# z videa výše si ale můžete stáhnout z mého Google Drive.

Co kdyby se to celé chovalo jako virtuální klávesnice?

No dobrá, ale co kdyby naše bezdotykové ovládání na straně PC vůbec žádný program nepotřebovalo? Co kdyby se to celé chovalo jako skutečná USB klávesnice? Jde to, některé mikrokontrolery totiž podporují USB instrukce HID. Jednoduše řečeno, některé mikrokontrolery se po připojení k PC umějí chovat jako klávesnice a myš.

To znamená, že Arduino může poslat skrze USB instrukci stisku klávesy doleva, doprava, nebo klidně i instrukci o pohybu a stisku myši. Ostatně už jsme si to v našem seriálu vyzkoušeli, když jsme si pomocí titěrného Digisparku vyrobili záškodnickou klávesnici.

Klepněte pro větší obrázek
Levný klon Pro Micro s čipem ATmega32U4 za 80 kaček z eBaye se může chovat jako USB myš a klávesnice. Když k němu připojím senzor gest, nebudu na PC potřebovat žádný další software.

Arduino Uno a jeho čip ATmega328P to ale neumí. Vedle Digisparku s nedostatkem pinů to ale zvládnou třeba Arduina, která jako řídící čip používají ATmega32U4 – třeba velké Leonardo, anebo malé Pro Micro od Sparkfunu, které je nicméně na naše pokusy docela drahé. Díky návrhu všech Arduin pod svobodnou licencí naštěstí opět stačí navštívit eBay a dohledat některý z laciných klonů. Když tedy v jeho katalogu vyhledáte frázi pro micro 32u4, narazíte na kousky začínající na částce pod stokorunou.

Práce s virtuální klávesnici je jednoduchá. Stačí použít vestavěnou knihovnu Keyboard a spustit ji v úvodní funkci setup:

Keyboard.begin();

Když bych pak třeba ve smyčce loop napsal tento kód:

Keyboard.print(“Jsem zly virus “);
delay(1000);

Bude se tento text vypisovat každou sekundu do libovolného textového pole, které bude mít zrovna fokus, tak dlouho, dokud bude mikropočítač připojený k PC.

Pomocí funkce Keyboard.write, pak mohu do PC poslat kód stisku jakékoliv klávesy, přičemž knihovna mi nabízí definice těch nejtypičtějších:

Keyboard.write(KEY_LEFT_ARROW);

Knihovna samozřejmě dále podporuje i kombinace kláves pro klávesové zkratky, a tak dokáže simulovat skutečnou klávesnici. Pro naše bezdotykové ovládání by tedy byla naprosto dostačující a Arduino by do PC zasílalo stisky bez potřeby dalšího softwaru.

S knihovnou Keyboard a Mouse lze provádět hromadu dalších psích kusů, o tom ale třeba zase někdy příště.

Témata článku: Software, Pojďme programovat elektroniku, Programování, Arduino, Kinect, Stavebnice, Drobná destička, Arduino Uno, Leap Motion, #define, Křemíkový obvod, PC klávesnice, Signal, Leonardo, Podmínka, Rising, Bezdotykové ovládání, Micro USB, Leap, Necelá hodina, Serial, Keyboard, Klávesnice k pc, Vyvolání, Virtuální klávesnice

Určitě si přečtěte


Aktuální číslo časopisu Computer

26 procesorů v důkladném testu

Zhodnotili jsme 18 bezdrátových reproduktorů

Jak fungují cash back služby?

Pohlídejte své děti na internetu